webentwicklung-frage-antwort-db.com.de

Senden großer Bilddaten über HTTP in Node.js

In meiner Entwicklungsumgebung habe ich zwei Server. Eine sendet ein Bild über eine POST http-Anfrage an die andere.

Client Server macht dies:

    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")
        })

Der Server, der die Anforderung empfängt, führt Folgendes aus:

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

Wenn ich ein kleines Bild sende, funktioniert es gut. Wenn ich jedoch ein großes Bild sende, obwohl die Datei korrekt gespeichert wurde, wird nur der erste obere Teil des Bildes angezeigt. Der Rest ist schwarz. Bildgröße ist korrekt.

Ich denke, es ist nur der erste Teil des Bildes, der in die Datei geschrieben wird. Ich habe versucht, eine readStream und eine writeStream zu erstellen, aber es scheint nicht zu funktionieren:

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

Kann ich direkt aus den Binärdaten und pipe in die Datei streamen? Für das, was ich gesehen habe, wird readStream häufig zum Streaming von Dateien und nicht von rohen Binärdaten verwendet.

Ich lese ein paar post s, aber es scheint für mich nicht zu funktionieren.

Ich verwende restler-Modul im Client-Server und restify im anderen.

Vielen Dank!

28
Maroshii

Tut mir leid, dass ich ehrlich bin, aber hier ist vieles falsch.

readFile liest den gesamten Inhalt einer Datei in den Speicher, bevor der Callback aufgerufen wird. Dann beginnt der Upload der Datei.

Dies ist insbesondere beim Umgang mit großen Dateien wie Bildern schlecht, da es wirklich keinen Grund gibt, die Datei in den Speicher zu lesen. Es ist verschwenderisch. und unter Last werden Sie feststellen, dass der Server nicht mehr über genügend Arbeitsspeicher verfügt und abstürzt.

Stattdessen möchten Sie ein Stream erhalten, das Datenblöcke ausgibt, wenn sie von der Festplatte gelesen werden. Alles, was Sie tun müssen, ist, diese Blöcke an Ihren Upload-Stream (pipe) weiterzuleiten und die Daten aus dem Speicher zu löschen. Auf diese Weise verwenden Sie niemals mehr als eine kleine Menge Pufferspeicher.

(Das Standardverhalten eines lesbaren Streams besteht darin, binäre Rohdaten zu verarbeiten. Nur wenn Sie eine encoding übergeben, behandelt es Text.)

Das request Modul macht dies besonders einfach:

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

Auf dem Server haben Sie ein größeres Problem. Verwenden Sie niemals * Sync Methoden. Es verhindert, dass Ihr Server irgendetwas (wie das Beantworten anderer Anforderungen) ausführt, bis die gesamte Datei auf die Festplatte geschrieben wird. Dies kann Sekunden dauern.

Stattdessen möchten wir den eingehenden Datenstrom in einen Dateisystemstrom leiten. Sie waren ursprünglich auf dem richtigen Weg. Der Grund dafür, dass req.body.pipe(fs.createWriteStream('test.png')) nicht funktioniert hat, ist, dass body kein Stream ist.

body wird von der Middleware bodyParser generiert. In restify verhält sich diese Middleware sehr ähnlich wie readFile, indem sie die gesamte eingehende Anforderungsentität im Speicher puffert. In diesem Fall wollen wir das nicht. Deaktivieren Sie die Body-Parser-Middleware.

Wo ist der eingehende Datenstrom? Es ist das req-Objekt selbst. Request von restify erbt den http.IncomingMessage des Knotens, einen lesbaren Stream. So:

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

Ich sollte auch erwähnen, dass dies alles so einfach funktioniert, weil es keinen Formular-Overhead gibt. Anfrage sendet einfach die Datei ohne multipart/form-data-Wrapper:

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

<image data>...

Dies bedeutet, dass ein Browser keine Datei an diese URL senden konnte. Wenn dies erforderlich ist, schauen Sie in formidable nach, was das Streaming von Request-Entities durchführt.

63
josh3736

Ich weiß nicht viel über Restler. Das Posten eines Bildes ist jedoch eine mehrteilige Anfrage.

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

Ich habe die obige Lösung ausprobiert und wenn Sie gerade hochgeladene Dateien oder etwas anderes verschoben haben, funktioniert Folgendes viel besser:

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

Ich habe Dateien über 200 MB hochgeladen und bei der Verwendung von Streams, Synchronisierung oder Asynchronisierung sind Fehler aufgetreten.

0
John Josef