在实际开发功能中,经常会有涉及到文件上传的需求。这里简单记录一下文件上传处理的sample。具体场景还需要进一步开发。
将整个报文体作为文件上传。(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"
分块传输编码(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"
默认情况下是 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);
});
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);
});
todo
链接: https://pan.baidu.com/s/1fM8VsI4u8iUTT-wt6Xf89A 提取码: y1sk
在experss 和client 分别 npm install 即可。然后阅读client代码及路由代码即可非常简单。
参考:
https://blog.csdn.net/wyn126/article/details/96451357