你可以通过Meteor实现文件上传,无需再使用任何软件包或第三方
选项1:DDP,将文件保存到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})
}
});
Explanation
首先,使用HTML5文件API从输入中获取文件.读卡器是使用新的FileReader创建的.该文件被读取为readAsArrayBuffer.这是arraybuffer,如果你愿意的话.log,返回{},DDP无法通过网络发送,因此必须将其转换为Uint8Array.
当你把这个放进Meteor 的时候.呼叫,Meteor自动运行EJSON.stringify(Uint8Array)并用DDP发送.你可以在chrome控制台websocket流量中查看数据,你会看到一个类似base64的字符串
在服务器端,Meteor呼叫EJSON.parse()并将其转换回缓冲区
Pros
- 简单,没有黑客手段,没有额外的软件包
- 坚持导线原理上的数据
Cons
- 更多带宽:生成的base64字符串比原始文件大约33%
- 文件大小限制:无法发送大文件(限制~16 MB?)
- 没有缓存
- 还没有gzip或压缩
- 如果发布文件,会占用大量内存
选项2:XHR,从客户端到文件系统的post
/*** 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
});
Explanation
抓取客户端中的文件,创建一个XHR对象,并通过"POST"将文件发送到服务器.
在服务器上,数据通过管道传输到底层文件系统.您还可以在保存前确定文件名、执行清理或判断文件是否已存在等.
Pros
- 利用XHR 2,您可以发送arraybuffer,与选项1相比,不需要新的FileReader()
- 与base64字符串相比,Arraybuffer的体积更小
- 没有大小限制,我在localhost中发送了一个约200 MB的文件,没有问题
- 文件系统比mongodb更快(下文的基准测试将介绍更多这方面的内容)
- Cachable和gzip
Cons
- XHR 2在较旧的浏览器中不可用,例如IE10以下的浏览器,但您当然可以实现传统的post<;表格>;我只使用了xhr=new XMLHttpRequest(),而不是HTTP.调用('POST'),因为当前HTTP.呼叫Meteor尚未发送arraybuffer(如果我错了,请告诉我).
- /path/to/dir/必须在meteor之外,否则在/public中写入文件会触发重新加载
选项3:XHR,保存到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);
});
Explanation
客户端脚本与选项2中的相同.
根据Meteor 1.0.x mongo_driver.js最后一行,一个名为MongoInternals的全局对象被公开,您可以调用defaultRemoteCollectionDriver()来返回GridStore所需的当前数据库db对象.在版本A中,GridStore也由MongoInternals公开.当前Meteor 使用的mongo是v1.4.x
然后在路由内部,可以通过调用var file=new GridStore(…)来创建一个新的写对象(API). 然后打开文件并创建一个流.
我还包括了一个版本B.在这个版本中,GridStore通过Npm使用一个新的mongodb驱动器来调用.require('mongodb'),这个mongo是最新的v2.截至本文 compose 时为0.13.新的API不需要您打开文件,您可以直接调用stream(true)并启动管道
Pros
- 与选项2相同,使用arraybuffer发送,与选项1中的base64字符串相比,开销更小
- 无需担心文件名清理
- 与文件系统分离,无需写入临时目录,数据库可以备份、rep、shard等
- 无需实施任何其他方案
- 可计算且可压缩
- 与普通mongo系列相比,可储存更大的尺码
- 使用管道减少内存过载
Cons
- Unstable Mongo GridFS. I included version A (mongo 1.x) and B (mongo 2.x). In version A, when piping large files > 10 MB, I got lots of error, including corrupted file, unfinished pipe. This problem is solved in version B using mongo 2.x, hopefully meteor will upgrade to mongodb 2.x soon
- API confusion.在版本A中,您需要先打开文件,然后才能进行流式处理,但在版本B中,您可以在不调用open的情况下进行流式处理.API文档也不是很清楚,流不是API confusion%可与Npm进行语法交换的.require('fs').在fs中,调用file.在('finish')上,但在GridFS中,您调用file.当书写完成/结束时打开('end').
- GridFS不提供写原子性,因此如果对同一个文件有多个并发写操作,最终结果可能会非常不同
- Speed.Mongo GridFS比文件系统慢得多.
Benchmark
file size GridFS FS
100 KB 50 2
1 MB 400 30
10 MB 3500 100
200 MB 80000 1240
你可以看到FS比GridFS快得多.对于一个200 MB的文件,使用GridFS大约需要80秒,而在FS中只需要1秒.我还没试过SSD,结果可能不一样.然而,在现实生活中,带宽可能会决定文件从客户端传输到服务器的速度,达到200 MB/秒的传输速度并不常见.另一方面,传输速度~2 MB/秒(GridFS)更为常见.
Conclusion
这并不全面,但你可以决定哪种 Select 最适合你的需要.
- DDP是最简单的,并坚持Meteor 核心原理,但数据更庞大,在传输过程中不可压缩,不可计算.但是如果你只需要小文件,这个选项可能会很好.
- XHR coupled with file system是"传统"的方式.稳定的API、快速的、可流动的、可压缩的、可缓存的(ETag等),但需要放在单独的文件夹中
- XHR coupled with GridFS,您可以获得rep set的好处,可扩展,不接触文件系统目录,大文件和许多文件,如果文件系统限制数量,也可压缩.然而,API是不稳定的,你会在多次写入中出错,这是..Lo、 ..W
希望很快,meteor DDP可以支持gzip、缓存等,GridFS可以达到faster...