Pomocí Meteoru můžete nahrát soubory bez použití dalších balíčků nebo třetí strany
Možnost 1:DDP, uložení souboru do sbírky 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})
}
});
Vysvětlení
Nejprve je soubor uchopen ze vstupu pomocí HTML5 File API. Čtečka je vytvořena pomocí nového FileReaderu. Soubor se čte jako readAsArrayBuffer. Tento arraybuffer, pokud console.log, vrací {} a DDP to nemůže poslat po drátě, takže musí být převeden na Uint8Array.
Když to vložíte do Meteor.call, Meteor automaticky spustí EJSON.stringify(Uint8Array) a odešle to s DDP. Data můžete zkontrolovat v provozu webového soketu konzoly Chrome, uvidíte řetězec připomínající base64
Na straně serveru Meteor zavolá EJSON.parse() a převede jej zpět na vyrovnávací paměť
Výhody
- Jednoduchý, žádný složitý způsob, žádné další balíčky
- Držte se principu Data on the Wire
Nevýhody
- Větší šířka pásma:výsledný řetězec base64 je ~ o 33 % větší než původní soubor
- Limit velikosti souboru:nelze odesílat velké soubory (limit ~ 16 MB?)
- Žádné ukládání do mezipaměti
- Zatím bez gzip nebo komprese
- Při publikování souborů zabere hodně paměti
Možnost 2:XHR, odeslání z klienta do systému souborů
/*** 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
});
Vysvětlení
Soubor v klientovi je uchopen, je vytvořen objekt XHR a soubor je odeslán pomocí 'POST' na server.
Na serveru jsou data přenesena do základního systému souborů. Před uložením můžete dodatečně určit název souboru, provést dezinfekci nebo zkontrolovat, zda již existuje atd.
Výhody
- Při využití výhod XHR 2, abyste mohli odesílat arraybuffer, není potřeba žádný nový FileReader() ve srovnání s možností 1
- Arraybuffer je méně objemný než řetězec base64
- Bez omezení velikosti, bez problémů jsem odeslal soubor ~ 200 MB v localhost
- Systém souborů je rychlejší než mongodb (více o tom později ve srovnání níže)
- Uložitelné do mezipaměti a gzip
Nevýhody
- XHR 2 není k dispozici ve starších prohlížečích, např. pod IE10, ale samozřejmě můžete implementovat tradiční příspěvek
- /path/to/dir/ musí být mimo meteor, jinak zápis souboru do /public spustí opětovné načtení
Možnost 3:XHR, uložit do 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);
});
Vysvětlení
Klientský skript je stejný jako u možnosti 2.
Podle posledního řádku Meteor 1.0.x mongo_driver.js je odhalen globální objekt nazvaný MongoInternals, můžete zavolat defaultRemoteCollectionDriver() a vrátit aktuální databázový db objekt, který je vyžadován pro GridStore. Ve verzi A je GridStore vystaven také MongoInternals. Mongo používané aktuálním meteorem je v1.4.x
Poté můžete uvnitř trasy vytvořit nový objekt pro zápis voláním var file =new GridStore(...) (API). Poté otevřete soubor a vytvořte stream.
Zahrnul jsem také verzi B. V této verzi se GridStore nazývá pomocí nového disku mongodb přes Npm.require('mongodb'), toto mongo je nejnovější v2.0.13 v době psaní tohoto článku. Nové API nevyžaduje, abyste soubor otevírali, můžete přímo zavolat stream(true) a spustit pipetování
Výhody
- Stejné jako u možnosti 2, odeslané pomocí arraybuffer, menší režie ve srovnání s řetězcem base64 u možnosti 1
- Nemusíte si dělat starosti s dezinfekcí názvu souboru
- Oddělení od systému souborů, není třeba zapisovat do dočasného adresáře, db lze zálohovat, opakovat, shard atd.
- Není třeba implementovat žádný další balíček
- Uložitelné do mezipaměti a lze je zazipovat
- V porovnání s běžnou sbírkou mongo skladujte mnohem větší velikosti
- Použití kanálu ke snížení přetížení paměti
Nevýhody
- Nestabilní Mongo GridFS . Zahrnul jsem verzi A (mongo 1.x) a B (mongo 2.x). Ve verzi A jsem při spojování velkých souborů> 10 MB dostal spoustu chyb, včetně poškozeného souboru, nedokončeného potrubí. Tento problém je vyřešen ve verzi B pomocí mongo 2.x, doufejme, že meteor brzy upgraduje na mongodb 2.x
- Zmatek v rozhraní API . Ve verzi A musíte před streamováním otevřít soubor, ale ve verzi B můžete streamovat bez volání open. Dokument API také není příliš jasný a stream není 100% zaměnitelný s Npm.require('fs'). V fs zavoláte file.on('finish'), ale v GridFS zavoláte file.on('end') při zápisu finishes/ends.
- GridFS neposkytuje atomicity zápisu, takže pokud existuje více souběžných zápisů do stejného souboru, konečný výsledek se může velmi lišit
- Rychlost . Mongo GridFS je mnohem pomalejší než souborový systém.
Srovnávací U možnosti 2 a možnosti 3 vidíte, že jsem zahrnul var start =Date.now() a při psaní end jsem console.log out čas v ms , níže je výsledek. Dual Core, 4 GB RAM, HDD, založené na ubuntu 14.04.
file size GridFS FS
100 KB 50 2
1 MB 400 30
10 MB 3500 100
200 MB 80000 1240
Můžete vidět, že FS je mnohem rychlejší než GridFS. U souboru o velikosti 200 MB to trvá ~80 sekund pomocí GridFS, ale pouze ~ 1 sekundu ve FS. SSD jsem nezkoušel, výsledek může být jiný. V reálném životě však může šířka pásma určovat, jak rychle je soubor streamován z klienta na server, dosažení přenosové rychlosti 200 MB/s není typické. Na druhou stranu je běžnější přenosová rychlost ~2 MB/s (GridFS).
Závěr
V žádném případě to není vyčerpávající, ale můžete se rozhodnout, která možnost je pro vaše potřeby nejlepší.
- DDP je nejjednodušší a drží se základního principu Meteor, ale data jsou objemnější, nelze je během přenosu komprimovat, nelze je uložit do mezipaměti. Ale tato možnost může být dobrá, pokud potřebujete pouze malé soubory.
- XHR ve spojení se systémem souborů je „tradiční“ způsob. Stabilní API, rychlé, 'streamovatelné', komprimovatelné, cachovatelné (ETag atd.), ale musí být v samostatné složce
- XHR ve spojení s GridFS , získáte výhodu sady opakování, škálovatelné, nedotýkající se adresáře systému souborů, velké soubory a mnoho souborů, pokud souborový systém omezuje počty, také komprimovatelné do mezipaměti. Rozhraní API je však nestabilní, při více zápisech dochází k chybám, je to s..l..o..w..
Doufejme, že brzy bude meteor DDP podporovat gzip, ukládání do mezipaměti atd. a GridFS může být rychlejší ...