web-dev-qa-db-ja.com

Node.jsでHTTPを介して大きな画像データを送信する

開発環境には2つのサーバーがあります。 1つはPOST http要求を介してもう1つに画像を送信します。

クライアントサーバーはこれを行います。

    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はしばしば生のバイナリデータではなくファイルからストリーミングするために使用されます。

私はいくつかの post sを読みましたが、私にはうまくいかないようです。

クライアントサーバーでrestlerモジュールを使用し、もう一方でrestifyモジュールを使用しています。

ありがとう!

28
Maroshii

率直に言ってすみませんが、ここには多くの間違いがあります。

readFile は、コールバックを呼び出す前にファイルの全体の内容をメモリに読み込みます、その時点でファイルのアップロードを開始します。

これは、特に画像のような大きなファイルを処理する場合、ファイルをメモリに読み込む理由がないため、悪いです。無駄だ。そして、負荷がかかると、サーバーのメモリが不足してクラッシュすることがわかります。

代わりに、streamを取得する必要があります。これは、ディスクから読み取られたデータのチャンクを放出します。必要なのは、これらのチャンクをアップロードストリーム(pipe)に渡してから、メモリからデータを破棄することだけです。このようにして、少量のバッファメモリしか使用しません。

(読み取り可能なストリームのデフォルトの動作は、生のバイナリデータを処理することです。テキストで処理するのは、encodingを渡す場合のみです。)

request モジュールはこれを特に簡単にします:

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

サーバーでは、より大きな問題があります。 * Syncメソッドを使用しないでください。サーバーの実行をブロックしますanything(likeファイル全体がディスクにフラッシュされるまで、他のリクエストに応答します。これには数秒かかる場合があります。

その代わりに、着信データストリームを取得し、それをファイルシステムストリームにパイプする必要があります。あなたはもともと正しい軌道に乗っていました。 req.body.pipe(fs.createWriteStream('test.png'))が機能しなかった理由は、bodyがストリームではないためです。

bodyは、bodyParserミドルウェアによって生成されます。 restifyでは、そのミドルウェアはreadFileのように機能し、着信リクエストエンティティ全体をメモリにバッファします。この場合、それは望ましくありません。ボディパーサーミドルウェアを無効にします。

では、着信データストリームはどこにありますか?それはreqオブジェクトそのものです。 restifyのRequestは、ノードの_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 を調べてください。

65
josh3736

私はレストラーについてあまり知りません。ただし、画像の投稿はマルチパートのリクエストです。

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

上記の解決策を試してみましたが、アップロードしたファイルなどを移動するだけの場合は、次の方法がはるかに効果的です。

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

200MBを超えるファイルをアップロードすると、ストリーム、同期または非同期を使用してエラーが発生しました。

0
John Josef