zoukankan      html  css  js  c++  java
  • 用Node处理文件上传

    前言

    在Web开发中,文件上传是一个非常常见、非常重要的功能。本文将介绍如何用Node处理上传的文件。

    需求分析

    由于现在前后端分离很流行,那么本文也直接采用前后端分离的做法。前端界面如下:
    图片描述

    用户从浏览器中选择文件,点击上传,将发起http请求到服务器,服务器将接受到的文件存储在服务器硬盘中。

    前端部分

    ajax请求库采用axios,为了简化说明,前端限制上传的文件类型只能为图片,且一次只能上传一张,有兴趣的朋友可以自行补充,代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    </head>
    <body>
      <input type="file" name="file" accept="image/*" onchange="changeImg(event)"/>
      <button onclick="submit()">上传</button>
    
      <script>
        let file = ''
        let fileName = ''
    
        function submit() {
          let data = new FormData()
          data.append('imgName', fileName)
          data.append('img', file)
    
          axios({
            method: 'post',
            timeout: 2000,
            url: 'http://localhost:3000/postApi',
            data: data
          })
            .then(response => {
              console.log(response.data)
            })
            .catch(error => {
              console.log(error)
            })
        }
    
        function changeImg(e) {
          file = e.target.files.item(0)
          // 如果不选择图片
          if (file === null) {
            return
          }
          fileName = file.name
        }
      </script>
    </body>
    </html>

    后端部分

    这是本文要介绍的重点,为了用高效流畅的方式来解析文件上传请求,我们先引入formidable库:

    npm install formidable --save

    formidable的流式解析器让它成为了处理文件上传的绝佳选择,也就是说它能随着数据块的上传接收它们,解析它们,并吐出特定的部分,相信熟悉流的朋友会很好理解。这种方式不仅快,还不会因为需要大量缓冲而导致内存膨胀,即便像视频这种大型文件,也不会把进程压垮。
    首先,我们在根目录下创建myImage文件,用于存放上传的图片(注意:如果没有创建,会导致上传报错),接着,我们创建一个IncomingForm实例form,并且设置存放路径为myImage文件夹。代码如下:

    var http = require('http')
    var formidable = require('formidable')
    
    var server = http.createServer(function(req, res){
      // 1 设置cors跨域
      res.setHeader('Access-Control-Allow-Origin', '*')
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
      res.setHeader('Content-Type', 'application/json')
    
      // 2
      switch (req.method) {
        case 'OPTIONS':
          res.statusCode = 200
          res.end()
          break
        case 'POST':
          upload(req, res)
          break
      }
    })
    
    function upload(req, res) {
      // 1 判断
      if (!isFormData(req)) {
        res.statusCode = 400
        res.end('错误的请求, 请用multipart/form-data格式')
        return
      }
    
      // 2 处理
      var form = new formidable.IncomingForm()
      form.uploadDir = './myImage'
      form.keepExtensions = true
    
      form.on('field', (field, value) => {
        console.log(field)
        console.log(value)
      })
      form.on('end', () => {
        res.end('上传完成!')
      })
    
      form.parse(req)
    }
    
    function isFormData(req) {
      let type = req.headers['content-type'] || ''
      return type.includes('multipart/form-data')
    }
    
    server.listen(3000)
    console.log('port is on 3000.')

    node app开启http服务器后,在前端页面中上传一张kitty.jpg,我们看到控制台打印出了前端上传的imgName属性:kitty.jpg
    图片描述

    并且,myImage文件夹目录下多了一张图片:
    图片描述

    打开一看,正是从前端上传的那张kitty.jpg

    文件改名

    我们发现,这个默认的文件名称并不是我们想要的,我们想改成以当前时间戳命名的文件,添加的功能代码如下:

      var fs = require('fs')
    
      form.on('file', (name, file) => {
        // 重命名文件
        let types = file.name.split('.')
        let suffix = types[types.length - 1]
        fs.renameSync(file.path, './myImage/' + new Date().getTime() + '.' + suffix)
      })

    再次上传,发现现在存的照片名称已经变成我们想要的格式了。
    图片描述

    添加上传进度

    Formidable的progress事件能给出收到的字节数,以及期望收到的字节数。我们可以借助这个做出一个进度条。
    我们为上面的程序添加下面的代码,每次有progress事件激发,就会计算百分比并用console.log()输出:

      form.on('progress', (bytesReceived, bytesExpected) => {
        var percent = Math.floor(bytesReceived / bytesExpected * 100)
        console.log(percent)
      })

    再次上传一张图片,现在控制台已经会打印出进度显示了:
    图片描述

    当然,一般情况下,我们是要把这个进度传回到用户的浏览器中去,这对于任何想要上传大型文件的程序来说是个很棒的特性,并且这是个很适合用Node完成的任务。比如说用WebSocket协议,或者像Socket.IO这样的实时模块,关于Node中使用websocket,后面我会单独出一篇文章来介绍。

    错误处理

    任何时候都不要忘了对程序添加错误处理,如果你的程序在重要的时候崩掉了,可能轻则被老板打屁股,重则拉出去祭天。想象一下,如果用户上传的图片很大,并且用户的网络还很慢,那么上传的时间会超出前端代码中设置的请求超时时间2s,服务器就会崩掉,不信?我们来试一下。
    首先,我选择了一张很大的图片,5M,并且用chrome浏览器将浏览器网络环境设置为slow 3g,设置方法如下:
    f12打开开发者工具,在more tools--network conditions
    图片描述

    图片描述

    点击上传,我们看见服务端控制台的信息如下,服务器崩掉了:
    图片描述

    所以,最后我们加上了错误处理,代码如下:

      // 加上错误处理,防止用户网络慢,或者取消上传,导致服务器崩掉
      form.on('error', err => {
        console.log(err)
        res.statusCode = 500
        res.end('服务器内部错误!')
      })

    小结

    现在,相信你已经学会了如何用Node处理文件上传了,结合前面的那篇用Node提供静态文件服务的文章,你是不是能够自己摸索着去尝试做一些有趣的事情了呢?

    参考阅读

    formidable文档
    input的file类型的accept属性

    本文转载于:猿2048https://www.mk2048.com/blog/blog.php?id=hbiij2jbh2j

  • 相关阅读:
    nodeJS学习(8)--- WS/...开发 NodeJS 项目-节3 <使用 mongodb 完整实例过程>
    nodeJS学习(7)--- WS开发 NodeJS 项目-节2 <安装&设置&启动 mongodb 数据库++遇到的问题>
    nodeJS学习(6)--- Sublime Text3 配置Node.js 开发环境
    nodeJS学习(5) --- sublime Text3 安装使用
    nodeJS学习(4)--- webstorm/...开发 NodeJS 项目-节1
    nodeJS学习(3)--- npm 配置和安装 express4.X 遇到的问题及解决
    二叉查找树-优化版,使用了指针引用
    二叉查找树实现-双向链表
    数据结构-中序转后序
    MySQL 游戏排行榜
  • 原文地址:https://www.cnblogs.com/10manongit/p/12784844.html
Copyright © 2011-2022 走看看