2012-03-30 4 views
5

Ich versuche eine App zu schreiben, die es meinen Benutzern ermöglicht, Dateien auf mein Google Cloud Storage-Konto hochzuladen. Um Überschreibungen zu vermeiden und einige benutzerdefinierte Handhabung und Protokollierung auf meiner Seite zu tun, verwende ich einen Node.js Server als Vermittler für den Upload. So ist der Prozess:Node.js POST-Datei zum Server

  1. Börse zu Node.js Server-Datei
  2. Node.js Server analysiert Datei, prüft Typ-Datei speichert einige Daten in DB
  3. Node.js Server-Uploads GCS-Datei
  4. Node.js Server-Antwort mit einem Pass auf Anforderung des Benutzers/Fail Bemerkung

ich auf Schritt 3, der gerade ein wenig verloren, wie diese Datei zu GCS zu senden. This question gibt einige hilfreiche Einblicke, sowie ein schönes Beispiel, aber ich bin immer noch verwirrt.

Ich verstehe, dass ich eine ReadStream für die temporäre Upload-Datei öffnen und diese an das http.request() Objekt leiten kann. Worüber ich verwirrt bin, ist, wie ich in meiner POST-Anfrage signalisiere, dass die piped Daten die file Variable sind. Nach der GCS API Docs, muss es eine file Variable geben, und es muss die letzte sein.

Also, wie gebe ich einen POST-Variablennamen für die Piped-Daten?

Bonuspunkte, wenn Sie mir sagen, wie es eher direkt von Upload Rohr meines Benutzers, als es in einer temporären Datei speichern

Antwort

4

Ich glaube, dass, wenn Sie tun POST mögen Sie verwenden müssen Content-Type: multipart/form-data;boundary=myboundary Kopfzeile. Und dann, im Körper, write() so etwas wie dies für jeden String Feld (Zeilenumbrüche sollten \r\n sein):

--myboundary 
Content-Disposition: form-data; name="field_name" 

field_value 

und dann für die Datei selbst, write() so etwas wie dies auf den Körper:

--myboundary 
Content-Disposition: form-data; name="file"; filename="urlencoded_filename.jpg" 
Content-Type: image/jpeg 
Content-Transfer-Encoding: binary 

binary_file_data 

Die binary_file_data ist, wo Sie verwenden pipe():

var fileStream = fs.createReadStream("path/to/my/file.jpg"); 
fileStream.pipe(requestToGoogle, {end: false}); 
fileStream.on('end, function() { 
    req.end("--myboundary--\r\n\r\n"); 
}); 

Die {end: false} 012 verhindertdie Anforderung automatisch schließen, da Sie nach dem Senden der Datei eine weitere Begrenzung schreiben müssen. Beachten Sie die zusätzlichen -- am Ende der Grenze.

Die große Sache ist, dass Google eine Header (sehr wahrscheinlich) benötigt. Wenn dies der Fall ist, können Sie keinen POST von Ihrem Benutzer an einen POST an Google streamen, da Sie nicht sicher wissen, was das ist, bis Sie die gesamte Datei erhalten haben.

Der Wert Header sollte eine einzige Zahl für den gesamten Körper sein. Der einfachste Weg ist, den ganzen Körper Buffer.byteLength(body) anzurufen, aber das wird schnell hässlich, wenn Sie große Dateien haben, und es wird auch das Streaming beendet.Eine Alternative wäre es zu berechnen, wie so:

var body_before_file = "..."; // string fields + boundary and metadata for the file 
var body_after_file = "--myboundary--\r\n\r\n"; 
var fs = require('fs'); 
fs.stat(local_path_to_file, function(err, file_info) { 
    var content_length = Buffer.byteLength(body_before_file) + 
      file_info.size + 
      Buffer.byteLength(body_after_file); 
    // create request to google, write content-length and other headers 
    // write() the body_before_file part, 
    // and then pipe the file and end the request like we did above 

Aber, die noch Ihre Fähigkeit tötet vom Benutzer zu streamen Google, hat die Datei auf der lokalen Festplatte heruntergeladen werden, um zu bestimmen es Länge ist.

Alternative Option

... nun, danach durch alle gehen, PUT könnte Ihr Freund hier sein. Gemäß https://developers.google.com/storage/docs/reference-methods#putobject können Sie einen transfer-encoding: chunked Header verwenden, so dass Sie die Dateilänge nicht finden müssen. Und ich glaube, dass der gesamte Körper der Anfrage nur die Datei ist, also können Sie pipe() verwenden und die Anfrage einfach beenden, wenn sie fertig ist. Wenn Sie https://github.com/felixge/node-formidable verwenden Uploads zu handhaben, dann können Sie etwas tun:

incomingForm.onPart = function(part) { 
    if (part.filename) { 
     var req = ... // create a PUT request to google and set the headers 
     part.pipe(req); 
    } else { 
     // let formidable handle all non-file parts 
     incomingForm.handlePart(part); 
    } 
} 
+0

Leider Google erfordert es eine POST-Anforderung, um zu tun, was ich versuche zu tun. Allerdings, große Antwort, und ich werde sicherlich die von Ihnen gemachten Vorschläge umsetzen. – jwegner

+0

Übrigens, node.js gibt Ihnen die Möglichkeit, [Stop-Pipe vom Schließen der Verbindung] (http://nodejs.org/api/stream.html#stream_stream_pipe_destination_options) – jwegner

+0

Guter Punkt, habe ich vergessen. Ich werde die Antwort bearbeiten, falls jemand anders darauf stößt. –