【问题标题】:Sending large image data over HTTP in Node.js在 Node.js 中通过 HTTP 发送大图像数据
【发布时间】:2013-02-06 18:01:40
【问题描述】:

在我的开发环境中,我有两台服务器。一个通过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")
        })
    })

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

我猜这只是写入文件的图像的第一块。 我尝试过创建readStreamwriteStream,但它似乎不起作用:

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

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

我读了几篇posts,但似乎对我不起作用。

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

谢谢!

【问题讨论】:

    标签: image node.js http


    【解决方案1】:

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

    restler.post("http://0.0.0.0:5000",{
        data: restler.file(path, filename, fileSize, encoding, contentType),
        multipart: true
    })
    

    【讨论】:

    • 使用 rest.file() 方法代替 fs.readFile。我会更新我的答案。
    【解决方案2】:

    对不起,这里有很多错误。

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

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

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

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

    request 模块使这变得特别容易:

    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 的Request 继承了node 的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,它对请求实体进行流式解析。

    【讨论】:

    • 很好的答案!感谢您回到正轨:) 我没有在问题中解释我需要完成什么:
    • 图片上传到服务器,调整为 4 种不同的尺寸,然后上传到第 3 方服务。我正在使用imagemagick 调整图像大小,我不确定是否可以传入stream,我必须更仔细地阅读文档。否则我必须阅读文件(?)我知道sync 方法不好,这只是一种调试方式。上述所有解释都适用于这一切吗?
    • 感谢您帮助前端新手... :)
    • @Maroshii:取决于你如何使用 imagemagick。如果你是invoking the CLI tools,它们完全在外部运行,你只需给它一个文件路径。因此,您只需在上传完成时调用 IM:fs.createWriteStream('test.png').pipe(req).on('end', function() { /* run imagemagick on test.png */ }); 还有一个 imagemagick-native 模块允许您对内存中的数据进行操作(因此不需要临时文件),但这要复杂得多。
    • @josh3736 如果不使用请求模块我怎么能做到这一点,你能帮我解决我的问题吗? stackoverflow.com/questions/38023426/…
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-07-27
      • 2012-11-14
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 2017-05-22
      • 1970-01-01
      • 2019-10-14
      • 1970-01-01
      相关资源
      最近更新 更多