Buscar..
Observaciones
El paquete CollectionFS ha sido archivado y descontinuado por su autor; sin embargo, ya que no hay un paquete alternativo en Atmosphere o el ecosistema Meteor para usar la funcionalidad GridFS de Mongo, y el código aún funciona perfectamente bien; Recomendamos no eliminar el ejemplo de la documentación de StackOverflow hasta que alguna otra solución GridFS pueda documentarse como reemplazo.
Investigación adicional
Filepicker.io Subidas y conversión de imágenes
Guardar patrón de archivo de Darío
Patrón de carga de archivos de Micha Roon
Paquete de carga de archivos EventedMind
Servidor / Cliente
Cargar archivos puede ser fácil o realmente complicado, dependiendo de lo que quieras hacer. En general, transferir un archivo en sí no es tan difícil. Pero hay muchos casos de borde alrededor de archivos adjuntos, archivos binarios y similares. Y el verdadero punto de fricción es la escala horizontal y la creación de una solución que funcione cuando el servidor se clona por segunda, tercera y novena vez.
Comencemos con un modelo básico de carga de servidor / cliente. Comenzamos agregando un elemento de entrada de archivo al modelo de objeto de documento.
<template name="example"> <input type=file /> </template>
Luego adjunte un evento al elemento de entrada dentro de su controlador y llame a un método local de Meteor `` startFileTransfer '' para iniciar la transferencia.
// client/example.js Template.example.events({ 'change input': function(ev) { _.each(ev.srcElement.files, function(file) { Meteor.startFileTransfer(file, file.name); }); } }); // client/save.js /** * @blob (https://developer.mozilla.org/en-US/docs/DOM/Blob) * @name the file's name * @type the file's type: binary, text (https://developer.mozilla.org/en-US/docs/DOM/FileReader#Methods) * * TODO Support other encodings: https://developer.mozilla.org/en-US/docs/DOM/FileReader#Methods * ArrayBuffer / DataURL (base64) */ Meteor.startFileTransfer = function(blob, name, path, type, callback) { var fileReader = new FileReader(), method, encoding = 'binary', type = type || 'binary'; switch (type) { case 'text': // TODO Is this needed? If we're uploading content from file, yes, but if it's from an input/textarea I think not... method = 'readAsText'; encoding = 'utf8'; break; case 'binary': method = 'readAsBinaryString'; encoding = 'binary'; break; default: method = 'readAsBinaryString'; encoding = 'binary'; break; } fileReader.onload = function(file) { Meteor.call('saveFileToDisk', file.srcElement.result, name, path, encoding, callback); } fileReader[method](blob); }
El cliente luego llamará al método del servidor saveFileToDisk, que realiza la transferencia real y pone todo en el disco.
// /** * TODO support other encodings: * http://stackoverflow.com/questions/7329128/how-to-write-binary-data-to-a-file-using-node-js */ Meteor.methods({ saveFileToDisk: function(blob, name, path, encoding) { var path = cleanPath(path), fs = __meteor_bootstrap__.require('fs'), name = cleanName(name || 'file'), encoding = encoding || 'binary', chroot = Meteor.chroot || 'public'; // Clean up the path. Remove any initial and final '/' -we prefix them-, // any sort of attempt to go to the parent directory '..' and any empty directories in // between '/////' - which may happen after removing '..' path = chroot + (path ? '/' + path + '/' : '/'); // TODO Add file existance checks, etc... fs.writeFile(path + name, blob, encoding, function(err) { if (err) { throw (new Meteor.Error(500, 'Failed to save file.', err)); } else { console.log('The file ' + name + ' (' + encoding + ') was saved to ' + path); } }); function cleanPath(str) { if (str) { return str.replace(/\.\./g,'').replace(/\/+/g,''). replace(/^\/+/,'').replace(/\/+$/,''); } } function cleanName(str) { return str.replace(/\.\./g,'').replace(/\//g,''); } } });
Ese es el tipo de enfoque escueto, y deja mucho que desear. Tal vez sea bueno para cargar un archivo CSV o algo así, pero eso es todo.
Dropzone (con hierro: enrutador)
Si queremos algo más pulido, con una interfaz de usuario de Dropzone integrada y un punto final REST, tendremos que comenzar a agregar rutas y paquetes REST personalizados con ayudantes de interfaz de usuario.
Comencemos importando Iron Router y Dropzone.
meteor add iron:router
meteor add awatson1978:dropzone
Y configure la ruta URL de carga que se especifica en el ayudante de la zona de descarga.
Router.map(function () {
this.route('uploads', {
where: 'server',
action: function () {
var fs = Npm.require('fs');
var path = Npm.require('path');
var self = this;
ROOT_APP_PATH = fs.realpathSync('.');
// dropzone.js stores the uploaded file in the /tmp directory, which we access
fs.readFile(self.request.files.file.path, function (err, data) {
// and then write the file to the uploads directory
fs.writeFile(ROOT_APP_PATH + "/assets/app/uploads/" +self.request.files.file.name, data, 'binary', function (error, result) {
if(error){
console.error(error);
}
if(result){
console.log('Success! ', result);
}
});
});
}
});
});
¡Guay! Tenemos un cargador de archivos con una interfaz de usuario elegante y un punto final REST programable. Desafortunadamente, esto no escala particularmente bien.
Filepicker.io
Para escalar las cosas, debemos dejar de usar el almacenamiento local en nuestro servidor y comenzar a usar un servicio de almacenamiento de archivos dedicado o implementar una capa de almacenamiento horizontal. La forma más fácil de comenzar con el almacenamiento de archivos escalable es usar una solución como Filepicker.io, que admite S3, Azure, Rackspace y Dropbox. loadpicker ha sido un unipackage popular de Filerpicker por un tiempo.
meteor add mrt:filepicker
El patrón de Filepicker es bastante diferente a las otras soluciones, porque en realidad se trata de integración de terceros. Comience agregando una entrada de filtro de archivos, que verá que se basa en gran medida en los atributos de datos *, que es un patrón bastante poco común en las aplicaciones Meteor.
<input type="filepicker" id="filepickerAttachment" data-fp-button-class="btn filepickerAttachment" data-fp-button-text="Add image" data-fp-mimetypes="image/*" data-fp-container="modal" data-fp-maxsize="5000000" data-fp-services="COMPUTER,IMAGE_SEARCH,URL,DROPBOX,GITHUB,GOOGLE_DRIVE,GMAIL">
También querrá establecer una clave de API, construir el widget del selector de archivos, activarlo y observar sus resultados.
if(Meteor.isClient){ Meteor.startup(function() { filepicker.setKey("YourFilepickerApiKey"); }); Template.yourTemplate.rendered = function(){ filepicker.constructWidget($("#filepickerAttachment")); } Template.yourTemplate.events({ 'change #filepickerAttachment': function (evt) { console.log("Event: ", evt, evt.fpfile, "Generated image url:", evt.fpfile.url); }); });
ColecciónFS
Sin embargo, si realmente desea el almacenamiento y desea almacenar millones de imágenes, necesitará aprovechar la infraestructura GridFS de Mongo y crear una capa de almacenamiento. Para eso, vas a necesitar el excelente subsistema CollectionFS.
Comience agregando los paquetes necesarios.
meteor add cfs:standard-packages
meteor add cfs:filesystem
Y agregando un elemento de carga de archivos a su modelo de objeto.
<template name="yourTemplate"> <input class="your-upload-class" type="file"> </template>
A continuación, agregue un controlador de eventos en el cliente.
Template.yourTemplate.events({
'change .your-upload-class': function(event, template) {
FS.Utility.eachFile(event, function(file) {
var yourFile = new FS.File(file);
yourFile.creatorId = Meteor.userId(); // add custom data
YourFileCollection.insert(yourFile, function (err, fileObj) {
if (!err) {
// do callback stuff
}
});
});
}
});
Y define tus colecciones en tu servidor:
YourFileCollection = new FS.Collection("yourFileCollection", {
stores: [new FS.Store.FileSystem("yourFileCollection", {path: "~/meteor_uploads"})]
});
YourFileCollection.allow({
insert: function (userId, doc) {
return !!userId;
},
update: function (userId, doc) {
return doc.creatorId == userId
},
download: function (userId, doc) {
return doc.creatorId == userId
}
});
Gracias a Raz por este excelente ejemplo. Querrá consultar la documentación completa de CollectionFS para obtener más detalles sobre lo que puede hacer todo CollectionFS.
Subidas del servidor
Los siguientes scripts son para cargar un archivo desde el sistema de archivos del servidor al servidor. Principalmente para archivos de configuración y observadores de archivos.
//https://forums.meteor.com/t/read-file-from-the-public-folder/4910/5
// Asynchronous Method.
Meteor.startup(function () {
console.log('starting up');
var fs = Npm.require('fs');
// file originally saved as public/data/taxa.csv
fs.readFile(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8', function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
data = JSON.parse(data);
console.log(data);
});
});
// Synchronous Method.
Meteor.startup(function () {
var fs = Npm.require('fs');
// file originally saved as public/data/taxa.csv
var data = fs.readFileSync(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8');
if (Icd10.find().count() === 0) {
Icd10.insert({
date: new Date(),
data: JSON.parse(data)
});
}
});
Meteor.methods({
parseCsvFile:function (){
console.log('parseCsvFile');
var fs = Npm.require('fs');
// file originally saved as public/data/taxa.csv
var data = fs.readFileSync(process.cwd() + '/../web.browser/app/data/taxa.csv', 'utf8');
console.log('data', data);
}
});