used subir imagenes filestore files example data are archivos almacenar mongodb file-upload meteor gridfs

mongodb - subir - Meteor: carga del archivo del cliente a la colección Mongo vs sistema de archivos vs GridFS



subir imagenes a mongodb (2)

Meteor es genial, pero carece de soporte nativo para la carga tradicional de archivos. Hay varias opciones para manejar la carga de archivos:

Desde el cliente , los datos se pueden enviar usando:

  • Meteor.call (''saveFile'', data) o collection.insert ({file: data})
  • Formulario ''POST'' o HTTP.call (''POST'')

En el servidor , el archivo se puede guardar en:

  • una colección de archivos mongodb de collection.insert ({file: data})
  • sistema de archivos en / ruta / a / dir
  • mongodb GridFS

¿Cuáles son los pros y los contras de estos métodos y la mejor manera de implementarlos? Soy consciente de que también hay otras opciones, como guardar en un sitio de un tercero y obtener una url.


Hola solo para agregar a Option1 con respecto a la visualización del archivo. Lo hice sin ejson.

<template name=''tryUpload''> <p>Choose file to upload</p> <input name="upload" class=''fileupload'' type=''file''> </template> Template.tryUpload.events({ ''change .fileupload'':function(event,template){ console.log(''change & view''); var f = event.target.files[0];//assuming upload 1 file only if(!f) return; var r = new FileReader(); r.onload=function(event){ var buffer = new Uint8Array(r.result);//convert to binary for (var i = 0, strLen = r.length; i < strLen; i++){ buffer[i] = r.charCodeAt(i); } var toString = String.fromCharCode.apply(null, buffer ); console.log(toString); //Meteor.call(''saveFiles'',buffer); } r.readAsArrayBuffer(f);};


Puede lograr la carga de archivos simplemente con Meteor sin usar más paquetes o un tercero

Opción 1: DDP, guardar el archivo en una colección mongo

/*** client.js ***/ // asign a change event into input tag ''change input'' : function(event,template){ var file = event.target.files[0]; //assuming 1 file only if (!file) return; var reader = new FileReader(); //create a reader according to HTML5 File API reader.onload = function(event){ var buffer = new Uint8Array(reader.result) // convert to binary Meteor.call(''saveFile'', buffer); } reader.readAsArrayBuffer(file); //read the file as arraybuffer } /*** server.js ***/ Files = new Mongo.Collection(''files''); Meteor.methods({ ''saveFile'': function(buffer){ Files.insert({data:buffer}) } });

Explicación

Primero, el archivo se toma de la entrada usando HTML5 File API. Se crea un lector usando el nuevo FileReader. El archivo se lee como readAsArrayBuffer. Este arraybuffer, si console.log, devuelve {} y DDP no puede enviarlo a través del cable, por lo que debe convertirse a Uint8Array.

Cuando pones esto en Meteor.call, Meteor ejecuta automáticamente EJSON.stringify (Uint8Array) y lo envía con DDP. Puede verificar los datos en el tráfico de websocket de la consola Chrome, verá una cadena que se asemeja a base64

En el lado del servidor, Meteor llama a EJSON.parse () y lo convierte de nuevo en buffer

Pros

  1. Simple, sin forma de hacky, sin paquetes adicionales
  2. Cumpla con el principio de Datos sobre el cable

Contras

  1. Más ancho de banda: la cadena base64 resultante es ~ 33% más grande que el archivo original
  2. Límite de tamaño de archivo: no se pueden enviar archivos grandes (¿límite ~ 16 MB?)
  3. Sin almacenamiento en caché
  4. Sin gzip o compresión aún
  5. Tome mucha memoria si publica archivos

Opción 2: XHR, publicación desde el cliente al sistema de archivos

/*** client.js ***/ // asign a change event into input tag ''change input'' : function(event,template){ var file = event.target.files[0]; if (!file) return; var xhr = new XMLHttpRequest(); xhr.open(''POST'', ''/uploadSomeWhere'', true); xhr.onload = function(event){...} xhr.send(file); } /*** server.js ***/ var fs = Npm.require(''fs''); //using interal webapp or iron:router WebApp.connectHandlers.use(''/uploadSomeWhere'',function(req,res){ //var start = Date.now() var file = fs.createWriteStream(''/path/to/dir/filename''); file.on(''error'',function(error){...}); file.on(''finish'',function(){ res.writeHead(...) res.end(); //end the respone //console.log(''Finish uploading, time taken: '' + Date.now() - start); }); req.pipe(file); //pipe the request to the file });

Explicación

Se toma el archivo del cliente, se crea un objeto XHR y el archivo se envía por ''POST'' al servidor.

En el servidor, los datos se canalizan a un sistema de archivos subyacente. También puede determinar el nombre del archivo, realizar la desinfección o verificar si ya existe, etc. antes de guardar.

Pros

  1. Aprovechando XHR 2 para poder enviar un arraybuffer, no se necesita un nuevo FileReader () en comparación con la opción 1
  2. Arraybuffer es menos voluminoso en comparación con la cadena base64
  3. Sin límite de tamaño, envié un archivo ~ 200 MB en localhost sin problemas
  4. El sistema de archivos es más rápido que mongodb (más de esto más adelante en la evaluación comparativa a continuación)
  5. Cachable y gzip

Contras

  1. XHR 2 no está disponible en navegadores más antiguos, por ejemplo, debajo de IE10, pero por supuesto puede implementar una publicación tradicional <form> I only use xhr = new XMLHttpRequest (), en lugar de HTTP.call (''POST'') porque el HTTP actual. llamada en Meteor todavía no puede enviar arraybuffer (señalarme si estoy equivocado).
  2. / path / to / dir / tiene que estar fuera del meteorito, de lo contrario escribir un archivo en / public dispara una recarga

Opción 3: XHR, guardar en GridFS

/*** client.js ***/ //same as option 2 /*** version A: server.js ***/ var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db; var GridStore = MongoInternals.NpmModule.GridStore; WebApp.connectHandlers.use(''/uploadSomeWhere'',function(req,res){ //var start = Date.now() var file = new GridStore(db,''filename'',''w''); file.open(function(error,gs){ file.stream(true); //true will close the file automatically once piping finishes file.on(''error'',function(e){...}); file.on(''end'',function(){ res.end(); //send end respone //console.log(''Finish uploading, time taken: '' + Date.now() - start); }); req.pipe(file); }); }); /*** version B: server.js ***/ var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db; var GridStore = Npm.require(''mongodb'').GridStore; //also need to add Npm.depends({mongodb:''2.0.13''}) in package.js WebApp.connectHandlers.use(''/uploadSomeWhere'',function(req,res){ //var start = Date.now() var file = new GridStore(db,''filename'',''w'').stream(true); //start the stream file.on(''error'',function(e){...}); file.on(''end'',function(){ res.end(); //send end respone //console.log(''Finish uploading, time taken: '' + Date.now() - start); }); req.pipe(file); });

Explicación

El script del cliente es el mismo que en la opción 2.

Según la última línea de Meteor 1.0.x mongo_driver.js , se expone un objeto global llamado MongoInternals, puede llamar a defaultRemoteCollectionDriver () para devolver el objeto db de base de datos actual que se requiere para GridStore. En la versión A, GridStore también está expuesto por MongoInternals. El mongo usado por el meteoro actual es v1.4.x

Luego, dentro de una ruta, puede crear un nuevo objeto de escritura llamando var file = new GridStore (...) ( API ). A continuación, abre el archivo y crea una secuencia.

También incluí una versión B. En esta versión, se llama a la GridStore utilizando una nueva unidad mongodb a través de Npm.require (''mongodb''), este mongo es el último v2.0.13 al momento de escribir esto. La nueva API no requiere que abras el archivo, puedes invocar stream (true) directamente y comenzar la canalización

Pros

  1. Lo mismo que en la opción 2, enviado utilizando arraybuffer, menos sobrecarga en comparación con la cadena base64 en la opción 1
  2. No hay necesidad de preocuparse por la sanitización del nombre de archivo
  3. Separación del sistema de archivos, no es necesario escribir en el directorio temporal, se puede hacer una copia de seguridad de la base de datos, rep, fragmento, etc.
  4. No es necesario implementar ningún otro paquete
  5. Cachable y puede ser comprimido
  6. Almacene tamaños mucho más grandes en comparación con la colección mongo normal
  7. Usar tubería para reducir la sobrecarga de memoria

Contras

  1. Inestable Mongo GridFS . Incluí la versión A (mongo 1.x) y B (mongo 2.x). En la versión A, cuando canalizo archivos de gran tamaño> 10 MB, recibí muchos errores, incluido un archivo dañado, un conducto sin terminar. Este problema se resuelve en la versión B usando mongo 2.x, con suerte meteor se actualizará a mongodb 2.x pronto
  2. Confusión de API . En la versión A, debe abrir el archivo antes de poder transmitir, pero en la versión B, puede transmitir sin llamar. El API doc tampoco es muy claro y la transmisión no es 100% sintaxis intercambiable con Npm.require (''fs''). En fs, llama a file.on (''finalizar'') pero en GridFS llama a file.on (''end'') cuando la escritura termina / finaliza.
  3. GridFS no proporciona atomicidad de escritura, por lo que si hay varias escrituras simultáneas en el mismo archivo, el resultado final puede ser muy diferente
  4. Velocidad Mongo GridFS es mucho más lento que el sistema de archivos.

Benchmark Puede ver en la opción 2 y la opción 3, incluí var start = Date.now () y al escribir end, configuro .log el tiempo en ms , a continuación se muestra el resultado. Dual Core, 4 GB ram, HDD, ubuntu 14.04 basado.

file size GridFS FS 100 KB 50 2 1 MB 400 30 10 MB 3500 100 200 MB 80000 1240

Puede ver que FS es mucho más rápido que GridFS. Para un archivo de 200 MB, toma ~ 80 segundos usando GridFS pero solo ~ 1 segundo en FS. No he probado SSD, el resultado puede ser diferente. Sin embargo, en la vida real, el ancho de banda puede dictar qué tan rápido se transmite el archivo de cliente a servidor, logrando una velocidad de transferencia de 200 MB / s no es típico. Por otro lado, una velocidad de transferencia ~ 2 MB / s (GridFS) es más la norma.

Conclusión

De ninguna manera esto es completo, pero puede decidir qué opción es mejor para su necesidad.

  • El DDP es el más simple y se adhiere al principio Meteor central, pero los datos son más voluminosos, no se pueden comprimir durante la transferencia, no se pueden almacenar en el caché. Pero esta opción puede ser buena si solo necesitas archivos pequeños.
  • XHR junto con el sistema de archivos es la forma ''tradicional''. API estable, rápida, ''streamable'', compresible, cachable (ETag, etc.), pero debe estar en una carpeta separada
  • XHR junto con GridFS , obtendrá el beneficio de un conjunto de repeticiones, escalable, sin tocar el directorio del sistema de archivos, archivos grandes y muchos archivos si el sistema de archivos restringe los números, también puede comprimirse. Sin embargo, la API es inestable, obtienes errores en varias escrituras, es s..l..o ... w ..

Esperemos que pronto, meteor DDP pueda soportar gzip, almacenamiento en caché, etc. y GridFS puede ser más rápido ...