2012-03-30 21 views
5

Estoy intentando escribir una aplicación que permita a mis usuarios subir archivos a mi cuenta de Google Cloud Storage. Para evitar sobrescrituras y hacer un manejo personalizado y el inicio de sesión de mi lado, estoy utilizando un servidor Node.js como intermediario para la carga. Por lo que el proceso es: ArchivoNode.js POST File to Server

  1. archivos de usuario a Node.js servidor
  2. Node.js servidor analiza el archivo, cheques Tipos de contenidos, almacena algunos datos en File-DB
  3. archivos del servidor
  4. Node.js a GCS
  5. respuesta del servidor
  6. Node.js a la solicitud del usuario con un pasa/no pasa observación

estoy un poco perdido en el paso 3, de exactamente cómo enviar el archivo a GCS. This question da una idea útil, así como un buen ejemplo, pero todavía estoy confundido.

Entiendo que puedo abrir un ReadStream para el archivo de carga temporal y canalizarlo al objeto http.request(). Lo que me confunde es cómo significo en mi solicitud POST que los datos canalizados son la variable file. De acuerdo con el GCS API Docs, debe haber una variable file, y debe ser la última.

Entonces, ¿cómo especifico un nombre de variable POST para los datos canalizados?

puntos de bonificación si usted me puede decir cómo tubería directamente de carga de mi usuario, en lugar de almacenarla en un archivo temporal

Respuesta

4

Creo que si usted quiere hacer el POST, usted tiene que utilizar una Content-Type: multipart/form-data;boundary=myboundary encabezado. Y luego, en el cuerpo, write() algo como esto para cada campo de cadena (saltos de línea debe ser \r\n):

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

field_value 

y luego por el propio archivo, write() algo como esto para el cuerpo:

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

binary_file_data 
El

binary_file_data es el que utiliza 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"); 
}); 

El {end: false} evita pipe() para cerrar automáticamente la solicitud porque necesita escribir un límite más después de que haya terminado de enviar el archivo. Tenga en cuenta el -- adicional en el extremo del límite.

El gran problema es que Google puede requerir un encabezado content-length (muy probable). Si ese es el caso, entonces no puede transmitir un POST de su usuario a un POST a Google porque no sabrá con seguridad qué es el content-length hasta que haya recibido el archivo completo.

El valor del encabezado content-length debe ser un número único para todo el cuerpo. La forma más sencilla de hacer esto es llamar al Buffer.byteLength(body) en todo el cuerpo, pero eso se pone feo rápidamente si tienes archivos grandes y también mata la transmisión.Una alternativa sería calcular que de este modo:

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 

embargo, que todavía mata a su capacidad para transmitir desde el usuario al Google, el archivo tiene que ser descargado en el disco local para determinar su longitud.

opción alternativa

... ahora, después de pasar por todo eso, PUT podría ser su amigo. De acuerdo con https://developers.google.com/storage/docs/reference-methods#putobject, puede usar un encabezado transfer-encoding: chunked, por lo que no necesita encontrar la longitud de los archivos. Y, creo que todo el cuerpo de la solicitud es solo el archivo, por lo que puede usar pipe() y simplemente dejar que finalice la solicitud cuando esté listo. Si está utilizando https://github.com/felixge/node-formidable para manejar archivos, a continuación, puede hacer algo como esto:

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

Desafortunadamente, Google exige que sea una petición POST con el fin de hacer lo que estoy tratando de hacer. Sin embargo, GRAN respuesta, y ciertamente implementaré las sugerencias que hizo. – jwegner

+0

Además, por cierto, node.js le da la capacidad de [detener el proceso de cierre de la conexión] (http://nodejs.org/api/stream.html#stream_stream_pipe_destination_options) – jwegner

+0

Buen punto, me olvidé de eso. Voy a editar la respuesta en caso de que alguien más se encuentre con ella. –