流星很棒,但缺乏对传统文件上传的原生支持。有几种方法可以处理文件上传:
从客户端,可以使用以下方式发送数据:
在服务器中,文件可以保存到:
这些方法有哪些优缺点以及如何最好地实施这些方法?我知道还有其他选项,例如保存到第三方网站并获取网址。
答案 0 :(得分:73)
您可以使用Meteor轻松实现文件上传,而无需使用任何其他软件包或第三方
/*** 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 强>
首先,使用HTML5 File API从输入中获取文件。使用新的FileReader创建阅读器。该文件读作readAsArrayBuffer。这个arraybuffer,如果你是console.log,返回{},而DDP不能通过网络发送,所以必须将它转换为Uint8Array。
当你把它放在Meteor.call中时,Meteor自动运行EJSON.stringify(Uint8Array)并用DDP发送它。您可以检查chrome console websocket流量中的数据,您将看到类似base64的字符串
在服务器端,Meteor调用EJSON.parse()并将其转换回缓冲区
<强>赞成强>
<强>缺点强>
/*** 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
});
解释
抓取客户端中的文件,创建XHR对象,并通过&#39; POST&#39;发送文件。到服务器。
在服务器上,数据通过管道传输到基础文件系统。在保存之前,您还可以确定文件名,执行消毒或检查是否已存在等。
<强>赞成强>
<强>缺点强>
/*** 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);
});
解释
客户端脚本与选项2中的相同。
根据Meteor 1.0.x mongo_driver.js最后一行,公开了一个名为MongoInternals的全局对象,您可以调用defaultRemoteCollectionDriver()来返回GridStore所需的当前数据库db对象。在版本A中,MongoInternals也公开了GridStore。当前流星使用的mongo是v1.4.x
然后在路由中,您可以通过调用var file = new GridStore(...)(API)来创建新的写入对象。然后打开文件并创建一个流。
我还包含了一个版本B.在这个版本中,GridStore是通过Npm.require(&#39; mongodb&#39;)使用新的mongodb驱动器调用的,这个mongo是最新的v2.0.13。 。新的API并不要求您打开文件,您可以直接调用stream(true)并开始管道
<强>赞成强>
<强>缺点强>
<强>基准强> 您可以在选项2和选项3中看到,我包括var start = Date.now(),当写入结束时,我在 ms 中记录时间,以下是结果。双核,4 GB RAM,HDD,ubuntu 14.04。
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)更为常态。
<强>结论强>
这绝不是全面的,但您可以决定哪种方案最适合您的需要。
希望很快,流星DDP可以支持gzip,缓存等,GridFS可以更快 ......
答案 1 :(得分:0)
您好,只需添加有关查看文件的Option1。我没有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);};