用node搭建文件上传服务的时候,浏览器端传来的二进制数据包括头部及尾部,如下:
------WebKitFormBoundaryAJSeHnZnCPWC4Avx
Content-Disposition: form-data; name="FileData"; filename="test.js"
Content-Type: application/javascript
console.log('test')
------WebKitFormBoundaryAJSeHnZnCPWC4Avx--
请问用pipe的时候,怎么获取到真正的数据呢?即console.log('test')
,我的node代码如下:
req.pipe(fs.createWriteStream(filepath + filename, {
encoding: 'utf-8'
}))
我最开始是监听req的data事件,但是传超大文件时,有可能超过V8内存限制而导致泄漏,所以想用pipe处理。
流式处理上传文件的话,可以参考下这个模块 co-busboy
@hyj1991 谢谢回答,我主要是想了解其中的原理,想自己写出来
我也挺想知道 solution 的,以下是能够重现楼主环境的程式。有兴趣的小伙伴欢迎一起来动动脑。
main.js
const http = require('http');
const fs = require('fs');
const app = http.createServer((req, res) => {
const writeStream = fs.createWriteStream('./output', { encoding: 'utf-8' });
req.pipe(writeStream);
return res.end('Write data in to output');
});
app.listen(3000);
test.js
console.log('test');
terminal
# terminal A 启动 node http server
> node main.js
# terminal B 发送上传 test.js 的 request
> curl --form "[email protected];type=application/javascript" http://localhost:3000
> cat ./output
--------------------------f05ad9c9cfdbd95d
Content-Disposition: form-data; name="data"; filename="test.js"
Content-Type: application/javascript
console.log('test');
--------------------------f05ad9c9cfdbd95d--
@grass0916 如果监听data方法的时候,可以处理二进制,但是有内存限制,还有一种方法是自己实现pipe方法,但是我觉得都有点麻烦,所以我发帖想问下,看是不是有更简单的办法
工作流程及原理:
Client 通过 formdata上传文件,后端从 header 中获取 boundary 就是你上面看到的 ‘----WebKitFormBoundaryAJSeHnZnCPWC4Avx’,request 是 readableStream,然后通过 boundary 切割流形成多个part (实战里每个formdata最多上传200多个part,多了就有问题了),每个 part 都是从 Content-Disposition: form-data; name=“FileData”; filename="test.js"那一行开始到下一个boundary之前。有 filename 的是文件流,没有 filename 的就是普通 filed。
PS:
formdata的格式是严格按照规范来的,如果想自己拼接 formdata 的话需要注意 boundary 的长度是固定值 52,还需要注意 /r/n 符号。可以自己打印一个标准的 formdata 看一下结构。后端校验 formdata 对不对最好在 header传上 content-length。nodejs 上传文件的库都是继承于 dicer 库(用 es5、eventEmitter 写的,遗憾的是我没把源码读完)
node 在流 这块处理用 pipe 真的很棒,文件就算很大,用 req.on(‘data’) 不断的写入文件也不至于内存爆掉(write这块是有个内置固定内存的buffer),你是不是都写入一个变量里了。
@mosaic101 多谢回复,最开始我用的是data事件时将所有有的buffer添加到内存,这样肯定是不行的,后来采用边读取边writeableStream,这种方式当读取的速度大于写入的速度的时候一样会爆内存,所以采用了pipe的方式,但是pipe方式无法处理传过来的二进制文件,无法提取到真正文件的数据流。