Meteor est génial mais il manque des supports natifs pour le téléchargement de fichiers traditionnel. Il existe plusieurs options pour gérer le téléchargement de fichiers:
Depuis le client , les données peuvent être envoyées à l'aide de:
Sur le serveur , le fichier peut être enregistré dans:
Quels sont les avantages et les inconvénients de ces méthodes et comment les appliquer au mieux? Je suis conscient qu'il existe également d'autres options telles que l'enregistrement sur un site tiers et l'obtention d'une URL.
Meteor vous permet de télécharger des fichiers très simplement avec Meteor sans utiliser de paquet supplémentaire ni un tiers.
/*** 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})
}
});
Explantion
Tout d'abord, le fichier est saisi à partir de l'entrée à l'aide de l'API de fichier HTML5. Un lecteur est créé en utilisant un nouveau FileReader. Le fichier est lu en tant que readAsArrayBuffer. Ce tampon de matrice, si vous console.log, renvoie {} et que DDP ne peut pas l'envoyer par câble, il doit donc être converti en Uint8Array.
Lorsque vous mettez cela dans Meteor.call, Meteor exécute automatiquement EJSON.stringify (Uint8Array) et l'envoie avec DDP. Vous pouvez vérifier les données dans le trafic websocket de la console chrome, vous verrez une chaîne ressemblant à base64
Meteor appelle EJSON.parse () côté serveur et le reconvertit en tampon
Avantages
Les inconvénients
/*** 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
});
Explication
Le fichier dans le client est saisi, un objet XHR est créé et le fichier est envoyé via "POST" au serveur.
Sur le serveur, les données sont acheminées dans un système de fichiers sous-jacent. Vous pouvez en outre déterminer le nom du fichier, effectuer une désinfection ou vérifier s'il existe déjà, etc. avant de l'enregistrer.
Avantages
Les inconvénients
/*** 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);
});
Explication
Le script client est le même que dans l'option 2.
Selon Meteor 1.0.x mongo_driver.js last, un objet global appelé MongoInternals est exposé. Vous pouvez appeler defaultRemoteCollectionDriver () pour renvoyer l’objet de base de données actuel requis pour GridStore. Dans la version A, le GridStore est également exposé par les MongoInternals. Le mongo utilisé par le météore actuel est v1.4.x
Ensuite, à l'intérieur d'une route, vous pouvez créer un nouvel objet d'écriture en appelant var file = new GridStore (...) ( API ). Vous ouvrez ensuite le fichier et créez un flux.
J'ai également inclus une version B. Dans cette version, le GridStore est appelé à l'aide d'un nouveau lecteur mongodb via Npm.require ('mongodb'), ce mongo est la dernière version v2.0.13 en date de cette écriture. Le nouveau API ne vous oblige pas à ouvrir le fichier, vous pouvez appeler stream (true) directement et démarrer la tuyauterie
Avantages
Les inconvénients
Benchmark Vous pouvez voir dans les options 2 et 3 que j’ai inclus var start = Date.now () et lors de l’écriture de fin, je console.log le temps passé dans ms, en-dessous est le résultat. Dual Core, 4 Go de RAM, disque dur, basé sur Ubuntu 14.04.file size GridFS FS
100 KB 50 2
1 MB 400 30
10 MB 3500 100
200 MB 80000 1240
Conclusion.
En aucun cas, ceci est complet, mais vous pouvez décider quelle option est la mieux adaptée à vos besoins.
.
Hopefully soon, meteor DDP can support gzip, caching etc and GridFS can be faster...
Bonjour, juste pour ajouter à Option1 concernant la visualisation du fichier. Je l'ai fait sans 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);};