【服务端知识点】(十五天)文件上传实现——30天node+mongo+react+redux+express搭建完美资源后台管理系统+前端(全栈工程师 结合antd)

时间:2021-08-11 14:56:36

这个系列文章是自己在开发过程中,自己感悟学习以及借鉴各方大师大神经验分享,非常尊重每个人的劳动成果,目的就是与大家共同分享学习,共同进步!
后台资源管理,增删改查操作必不可少,其中增加资源又是一切的根本,增加的资源的方式有很多种,
1.上传文件 2.复制链接 3.库存导入 4.其他

那今天要和大家要和大家分享的就是最主动地,最基础的方式,自主上传内容这种形式。今天要介绍的方法有三种,留个悬念,开始吧。

一、node+express实现文件上传的常用方式

常用的实现上传的方式有:
1.express中间件multer模块(此效率最高,在express3.x原生支持,到了express4.x独立成一个模块了)
2.connect-multiparty模块(但现在 官方不推荐 )
3.使用multiparty模块实现(此方法比较普遍)
4.使用formidable插件实现(插件呢,就是简单易懂);

二、了解multipart/form-data

首先知道enctype这个属性管理的是表单的MIME编码。共有三个值可选:
1.application/x-www-form-urlencoded
2.multipart/form-data
3.text/plain

1.application/x-www-form-urlencoded是默认值,作用是设置表单传输的编码。
例如我们在AJAX中见过 xmlHttp.setRequestHeader("Content-Type","application/x-www-form- urlencoded");如果不写会报错的,但是在html的form表单里是可以不写enctype=application/x-www- form-urlencoded,因为默认的HTML表单就是这种传输编码类型的。

2.multipart/form-data是用来制定传输数据的特殊类型的,主要就是我们上传的非文本的内容,比如图片或是是mp3等等。
3.text/plain是纯文本传输的意思
在发邮件的时候要设置这种编码类型,否则会出现接收时编码混乱的问题。网络上经常拿text /plain和 text/html做比较,其实这两个很好区分,前者用来传输纯文本文件,后者则是传递html代码的编码类型,在发送头文件时才用得上。①和③都不能用于上传文件,只有multipart/form-data才能完整的传递文件数据。

当我们采用enctype='multipart/form-data' 会以request payload提交数据,如图

三、multer模块上传问题

github地址: https://github.com/expressjs/multer https://www.npmjs.com/package/multer

Multer是node的一个中间件,通过multipart/form-data类型提交,如果在顶部写入busboy模块(可以快速解析来自html的数据)可以加快效率。

安装multer:

npm install --save multer

官方基本例子:

var express = require('express')
var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })

var app = express()

app.post('/profile', upload.single('avatar'), function (req, res, next) {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
})

app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
  // req.files is array of `photos` files
  // req.body will contain the text fields, if there were any
})

var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
  //
  // e.g.
  //  req.files['avatar'][0] -> File
  //  req.files['gallery'] -> Array
  //
  // req.body will contain the text fields, if there were any
})

也可以通过:
var upload = multer({dest:"uploads/"}).single('avatar');
app.post('/profile', function (req, res) {
  upload(req, res, function (err) {
  if (err) {
    console.log(req.body);   //打印请求体
    console.log(req.file);   
    // An error occurred when uploading
    return
  }
  // Everything went fine
  })
})

四、multiparty模块上传问题

github地址: https://github.com/andrewrk/node-multiparty

使用multiparty模块,也是必须要使用"multipart/form-data"类型,通过busboy模块可以加快解析效率。

安装multiparty:

npm install multiparty

其实这个也比较简单:

var multiparty = require('multiparty');
var http = require('http');
var util = require('util');
var fs = require("fs");

http.createServer(function(req, res) {
  if (req.url === '/upload' && req.method === 'POST') {
    // 解析一个文件上传
    var form = new multiparty.Form();
   //设置编辑
   form.encoding = 'utf-8';
   //设置文件存储路径
    form.uploadDir = "uploads/images/";
  //设置单文件大小限制 
  form.maxFilesSize = 2 * 1024 * 1024;
  //form.maxFields = 1000;  设置所以文件的大小总和
  form.parse(req, function(err, fields, files) {
    console.log(files.originalFilename);
    console.log(files.path);
    //同步重命名文件名
   fs.renameSync(files.path,files.originalFilename);
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });

    return;
  }

  // show a file upload form
  res.writeHead(200, {'content-type': 'text/html'});
  res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
}).listen(8080);

五、formidable模块上传问题

formidable上传插件,也是在github上同类功能人气比较高的。

github地址: https://github.com/felixge/node-formidablehttps://www.npmjs.org/package/formidable

优点:

1. 速度快(~500M/s),没有non-buffering multipart解析

2.自动写入到上传文件磁盘

3.占用内存低

4.优雅的错误处理

5.非常高的测试覆盖率

formidable安装:

npm install formidable@latest

官方例子:

var formidable = require('formidable'),
    http = require('http'),
    util = require('util');

http.createServer(function(req, res) {
  if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
    //创建表单上传
    var form = new formidable.IncomingForm();
    //设置编辑
    form.encoding = 'utf-8';
    //设置文件存储路径
    form.uploadDir = "uploads/images/";
    //保留后缀
    form.keepExtensions = true;
    //设置单文件大小限制    
    form.maxFieldsSize = 2 * 1024 * 1024;
    //form.maxFields = 1000;  设置所以文件的大小总和

    form.parse(req, function(err, fields, files) {//其中files中包含 File对象,是一个h5的新数据类型,可参考w3c File api。这块我踩了很久的坑
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('服务器收到了');
      res.end(util.inspect({fields: fields, files: files}));
    });

    return;
  }

  // show a file upload form
  res.writeHead(200, {'content-type': 'text/html'});
  res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
}).listen(8080);
其中 formidable FIle模块的详细说明见下
 

1)     创建Formidable.IncomingForm对象
   var form = new formidable.IncomingForm()
2)     form.encoding = 'utf-8' 设置表单域的编码
3)     form.uploadDir = "/my/dir"; 设置上传文件存放的文件夹,默认为系统的临时文件夹,可以使用fs.rename()来改变上传文件的存放位置和文件名
4)     form.keepExtensions = false; 设置该属性为true可以使得上传的文件保持原来的文件的扩展名。
5)     form.type 只读,根据请求的类型,取值'multipart' or 'urlencoded'
6)     form.maxFieldsSize = 2 * 1024 * 1024; 限制所有存储表单字段域的大小(除去file字段),如果超出,则会触发error事件,默认为2M
7)     form.maxFields = 1000 设置可以转换多少查询字符串,默认为1000
8)     form.hash = false; 设置上传文件的检验码,可以有两个取值'sha1' or 'md5'.
9)     form.multiples = false; 开启该功能,当调用form.parse()方法时,回调函数的files参数将会是一个file数组,数组每一个成员是一个File对象,此功能需要 html5中multiple特性支持。
10)   form.bytesReceived 返回服务器已经接收到当前表单数据多少字节
11)   form.bytesExpected 返回将要接收到当前表单所有数据的大小
12)   form.parse(request, [callback]) 该方法会转换请求中所包含的表单数据,callback会包含所有字段域和文件信息,如:
    form.parse(req, function(err, fields, files) {
      // ...   
    });
13)    form.onPart(part); 你可以重载处理multipart流的方法,这样做的话会禁止field和file事件的发生,你将不得不自己处理这些事情,如:
     form.onPart = function(part) {
       part.addListener('data', function() {
                 // ...
       });
   }
    如果你只想让formdable处理一部分事情,你可以这样做:
    form.onPart = function(part) {
      if (!part.filename) {
           // 让formidable处理所有非文件部分
           form.handlePart(part);
      }
   }
14)   formidable.File对象
  A.      file.size = 0 上传文件的大小,如果文件正在上传,表示已上传部分的大小
  B.      file.path = null 上传文件的路径。如果不想让formidable产生一个临时文件夹,可以在fileBegain事件中修改路径
  C.      file.name = null 上传文件的名字
  D.     file.type = null 上传文件的mime类型
  E.      file.lastModifiedDate = null 时间对象,上传文件最近一次被修改的时间
  F.      file.hash = null 返回文件的hash值
  G.     可以使用JSON.stringify(file.toJSON())来格式化输出文件的信息
15)   form.on('progress', function(bytesReceived, bytesExpected) {}); 当有数据块被处理之后会触发该事件,对于创建进度条非常有用。
16)   form.on('field', function(name, value) {}); 每当一个字段/值对已经收到时会触发该事件
17)   form.on('fileBegin', function(name, file) {});  在post流中检测到任意一个新的文件便会触发该事件
18)   form.on('file', function(name, file) {}); 每当有一对字段/文件已经接收到,便会触发该事件
19)   form.on('error', function(err) {}); 当上传流中出现错误便会触发该事件,当出现错误时,若想要继续触发request的data事件,则必须手动调用request.resume()方法
20)   form.on('aborted', function() {}); 当用户中止请求时会触发该事件,socket中的timeout和close事件也会触发该事件,当该事件触发之后,error事件也会触发
21) form.on('end', function() {}); 当所有的请求已经接收到,并且所有的文件都已上传到服务器中,该事件会触发。此时可以发送请求到客户端。
注:本文译自https://www.npmjs.com/package/formidable