nodejs 处理文件上传(express)

在实际开发功能中,经常会有涉及到文件上传的需求。这里简单记录一下文件上传处理的sample。具体场景还需要进一步开发。


服务端:使用express框架处理文件上传
Client:使用POSTMAN进行文件上传/使用nodejs request 编写http请求

1 使用binary方式上传

将整个报文体作为文件上传。(POST/PUT 方法)
这种情况用于已知文件大小时传输。
express 服务端代码:

const express = require('express');
const router = express.Router();
const fs = require('fs');

router.post('/upload', (req, res, next) => {
  console.log(req.headers);
  let buffers = [];
  req.on('data',(trunk) => {
    console.info(trunk.length);
    buffers.push(trunk);
  }).on('end',async () => {
      const buffer= Buffer.concat(buffers);
      fs.writeFileSync('test.mp4', buffer);
      return res.end();
    }).on('close', () => {
      res.status(400).json({"err_code":"40000"});
    }).on('error', () => {
      res.status(400).json({"err_code":"40000"});
    });
})

将接受到所有数据写入文件中。
**
POSTMAN 上传选择bin时 HTTP headers:

accept:"*/*"
accept-encoding:"gzip, deflate, br"
cache-control:"no-cache"
connection:"keep-alive"
content-length:"7995707"
content-type:"video/quicktime"
host:"localhost:12000"
postman-token:"79ef8202-d390-4496-bbce-0b3cd59ad71b"
user-agent:"PostmanRuntime/7.22.0"

使用nodejs request模块上传

const fs = require('fs');
const request = require("request");
const video = fs.readFileSync('./test.mp4');

var options = { method: 'POST',
  url: 'http://localhost:12000/file/upload',
  body:video
};

request(options, function (error, response, body) {
  if (error) {
    console.error(error);
  }
  console.log(response.statusCode);
});

http headers:·

connection:"close"
content-length:"2897147"
host:"localhost:12000"

2 Chunked方式上传

分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由网页服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。
在不知道数据量有多大的情况下可以使用chunked来进行数据传输

1 HTTP的头部有Transfer-Encoding: chunked
2 HTTP的body中组成: 编码使用若干个chunk组成,由一个标明长度为0的chunk结束。
每个chunk有两部分组成,第一部分是该chunk的长度,第二部分就是指定长度的内容,每个部分用CRLF隔开。在最后一个长度为0的chunk中的内容是称为footer的内容,是一些没有写的头部内容。
chunk格式如下:
[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]

chunk size是以十六进制的ASCII码表示,比如:头部是3134这两个字节,表示的是1和4这两个ascii字符,被http协议解释为十六进制数14,也就是十进制的20,后面紧跟[\r\n](0d 0a),再接着是连续的20个字节的chunk正文。chunk数据以0长度的chunk块结束,也就是(30 0d 0a 0d 0a)。

Server 代码同1.1 中代码, express req.on(‘data’)会自行处理chunked 这种数据方式,触发data直接就是解析chunked后的真正数据

Server收到的headers 如下:

connection:"close"
host:"localhost:12000"
transfer-encoding:"chunked"

nodejs request请求代码:

const fs = require('fs');
const request = require("request");
const videoFile = fs.readFileSync('./test.mp4');
const video = Buffer.from(videoFile);
console.info(videoFile.length);
const video1 = video.slice(0, 1000000); 
const video2 = video.slice(1000000, 2000000); 
const video3 = video.slice(2000000); 

const http = require('follow-redirects').http;
var options = {
  'method': 'POST',
  'hostname': 'localhost',
  'port':'12000',
  'path':'/file/upload',
  'maxRedirects': 20
};
var req = http.request(options, function (res) {
  var chunks = [];
  res.on("data", function (chunk) {
    chunks.push(chunk);
  });

  res.on("end", function (chunk) {
    console.log("------------------");
    var body = Buffer.concat(chunks);
    console.log(body.toString());
  });

  res.on("error", function (error) {
    console.error(error);
  });
});
// var postData = video;
req.write(video1);
req.write(video2);
req.write(video3);

req.end();

这里代码没有填写Content-Length 所以这里默认使用chunked 方式传输,如果这类填入content-Length
即上诉代码中options修改为:

var options = {
  'method': 'POST',
  'hostname': 'localhost',
  'port':'12000',
  'path':'/file/upload',
  'maxRedirects': 20,
  headers:{
    'Content-Length':videoFile.length
  }
};

则会禁用chunked方式传输。但实际上数据还是分段写入,这种用法可以用去嵌入式设备。比如内存有限的情况下不能将一个文件读出来再调用API进行发送,这里就可以读一点发一点,最后嗲用res.end().
这里headers 如下:

connection:"close"
content-length:"2897147"
host:"localhost:12000"

3 x-www-urlencoded 对值进行base64编码上传文件

默认情况下是 application/x-www-urlencoded,当表单使用 POST 请求时,数据会被以 x-www-urlencoded 方式编码到 Body 中来传送。即会被编码成价值对,如比如name=java&age
= 23。 x-www-urlencoded好像仅支持ASCII字符集。
原来使用百度API时,用于识别的图片文件就被base64编码成一个字段值进行传输。但这种数据传输仅适用于小文件,因为base64对2进制数据编码会造成文件大小增加。影响传输效率。
所以下列代码 name=imageName&image=imageDataBase64 将图片数据传输给server。

普通的 HTML Form POST请求,它会在头信息里使用 Content-Length 注明内容长度。请求头信息每行一条,空行之后便是 Body,即“内容”(entity)。内容的格式是在头信息中的 Content-Type 指定的,如上是 application/x-www-form-urlencoded,这意味着消息内容会经过 URL 格式编码

express 处理代码

router.post('/base64', (req, res, next) => {
  console.info(req.headers);
  console.info(req.body.image.length);
  const base64Data = req.body.image.replace(/\s/g,"+");
  const imageData = Buffer.from(base64Data, 'base64');//解码图片
  fs.writeFileSync('test.png', imageData);
  res.end();
})

Http headers

connection:"close"
content-length:"2006"
content-type:"application/x-www-form-urlencoded"
host:"localhost:12000"

nodejs request代码

const fs = require('fs');
const request = require("request");
const imageFile = fs.readFileSync('./test.png');
let base64str = Buffer.from(imageFile, 'binary').toString('base64');
const body = {
  name: 'test.png',
  image: base64str
}
var options = { method: 'POST',
  url: 'http://localhost:12000/file/base64',
  form: body
};
request(options, (error, response, body) => {
  if (error) {
    console.error(error);
  }
  console.log(response.statusCode);
  console.log(body);
});

4 表单上传

form-data:
http请求中的multipart/form-data,会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。
既可以上传键值对,也可以上传文件。当上传的字段是文件,会使用content-type表明文件类型;content-disposition说明字段的一些信息。
由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对。

下面举个例子:

POST /file/formdata HTTP/1.1
Host: localhost:12000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"

haha
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="video"; filename="/E:/source/develop/fileuploadserver/uploadclient/test.mp4"
Content-Type: <Content-Type header here>

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="image"; filename="/E:/source/develop/fileuploadserver/uploadclient/test.png"
Content-Type: image/png

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW

可以看到body之间使用content-Type中 boundary 分隔符进行分隔。

接受form-data编码的数据,其中有三个字段name video image, video image为文件。
express处理代码:这里使用multer模块

const express = require('express');
const router = express.Router();
const multer = require('multer'); // v1.0.5
const fs = require('fs');
const storage  = multer.memoryStorage();
var upload = multer({storage: storage})
router.post('/formdata', function(req, res, next) {
    console.log(req.headers);
    upload.fields([{name:'video', maxCount:1}, { name:'image', maxCount: 1}])(req, res, (err) => {//接受文件上传
      console.log(req.files.video[0]);
      console.log(req.files.image[0]);
      fs.writeFileSync('test.MOV', req.files.video[0].buffer);
      fs.writeFileSync('test.png', req.files.image[0].length);
      console.log(req.body.name);
      res.end();
    });
})

HTTP headers 请求headers

connection:"close"
content-length:"2898918"
content-type:"multipart/form-data; boundary=--------------------------230104090310630847651902"
host:"localhost:12000"

nodejs request 请求

const fs = require('fs');
const request = require("request");
const formData = {
  name: 'haha',
  video: fs.createReadStream('./test.mp4'),
  image: fs.createReadStream('./test.png'),
};
var options = { method: 'POST',
  url: 'http://localhost:12000/file/formdata',
  formData,
};
// console.info(options);
request(options, (error, response, body) => {
  if (error) {
    console.error(error);
  }
  console.log(response.statusCode);
  console.log(body);
});

5 断点续传(分块并发上传)/大文件处理

todo

6 示例代码

链接: https://pan.baidu.com/s/1fM8VsI4u8iUTT-wt6Xf89A 提取码: y1sk

在experss 和client 分别 npm install 即可。然后阅读client代码及路由代码即可非常简单。

参考:
https://blog.csdn.net/wyn126/article/details/96451357

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值