nodejs中的pipe问题
发布于 6 个月前 作者 xudeming208 1909 次浏览 来自 问答

用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处理。

8 回复

流式处理上传文件的话,可以参考下这个模块 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 "data=@test.js;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方式无法处理传过来的二进制文件,无法提取到真正文件的数据流。

同问,我从busboy源码找到这里/lib/types/multipart.js,但是没看懂。


再看的时候发现这个模块dicer,好像有惊喜,我先去看看。

回到顶部