29

在我的开发环境中,我有两台服务器。POST一个通过http 请求向另一个发送和图像。

客户端服务器这样做:

    fs.readFile(rawFile.path,'binary',function (err, file){
        restler.post("http://0.0.0.0:5000",{
            data: file,
            headers:{
                "Content-Type": rawFile.type,
            }
        }).on('complete',function(data,response){                               
            console.log(data);
            res.send("file went through")
        })

接收请求的服务器执行以下操作:

    server.post('/',function(req,res,next){
        fs.writeFileSync("test.png",req.body,"binary",function(err){
            if(err) throw err;
            res.send("OK")
        })
    })

如果我发送一个小图像,它工作正常。但是,如果我发送一个大图像,虽然文件保存正确,但只显示图像的第一上部。其余为黑色。图像大小正确。

我想这只是被写入文件的第一块图像。我试过创建 areadStream和 awriteStream但它似乎不起作用:

req.body.pipe(fs.createWriteStream('test.png'))

我可以直接从二进制数据流式传输pipe到文件中吗?就我所见,readStream通常用于从文件而不是原始二进制数据中流式传输。

我读了一些帖子,但它似乎对我不起作用。

restler在客户端服务器和restify另一个中使用模块。

谢谢!

4

3 回答 3

69

很抱歉直言不讳,但这里有很多错误。

readFile在调用回调之前将文件的全部内容读入内存,此时您开始上传文件。

这很糟糕——尤其是在处理像图像这样的大文件时——因为实际上没有理由将文件读入内存。这很浪费;在负载下,您会发现您的服务器将耗尽内存并崩溃。

相反,您想获得一个stream,它在从磁盘读取数据时发出数据块。您所要做的就是将这些块传递到您的上传流 ( pipe),然后从内存中丢弃数据。通过这种方式,您永远不会使用超过少量的缓冲内存。

(可读流的默认行为是处理原始二进制数据;只有当您传递一个encoding时,它才会处理文本。)

请求模块使这变得特别容易:

fs.createReadStream('test.png').pipe(request.post('http://0.0.0.0:5000/'));

在服务器上,你有一个更大的问题。 永远不要使用 *Sync方法。 它会阻止您的服务器执行任何操作(例如响应其他请求),直到整个文件刷新到磁盘,这可能需要几秒钟。

因此,我们希望获取传入的数据流并将其通过管道传输到文件系统流。你原本是在正确的轨道上;req.body.pipe(fs.createWriteStream('test.png'))不起作用的原因是因为body不是流。

bodybodyParser中间件生成。在 restify 中,该中间件的行为很像readFile,它将整个传入的请求实体缓冲在内存中。在这种情况下,我们不希望这样。禁用正文解析器中间件。

那么传入的数据流在哪里呢?它是req对象本身。restify'sRequest继承 node's http.IncomingMessage,这是一个可读流。所以:

fs.createWriteStream('test.png').pipe(req);

我还应该提到,这一切都很简单,因为不涉及表单解析开销。请求只是发送没有multipart/form-data包装器的文件:

POST / HTTP/1.1
host: localhost:5000
content-type: application/octet-stream
Connection: keep-alive
Transfer-Encoding: chunked

<image data>...

这意味着浏览器无法将文件发布到此 URL。如果需要,请查看formidable,它对请求实体进行流式解析。

于 2013-02-21T16:58:17.137 回答
0

我不太了解restler。但是发布图像是一个多部分的请求。

restler.post("http://0.0.0.0:5000",{
    data: restler.file(path, filename, fileSize, encoding, contentType),
    multipart: true
})
于 2013-02-21T12:15:31.650 回答
0

我尝试了上面的解决方案,如果您只是移动上传的文件或以下内容,效果会更好:

fs.rename(path, newPath, callback(err) {});

我正在上传超过 200MB 的文件,并且在使用流、同步或异步时会遇到错误。

于 2013-09-06T18:38:00.790 回答