ServerClient
上傳檔案很簡單或非常複雜,具體取決於你想要做什麼。通常,傳輸檔案本身並不困難。但是附件,二進位制檔案等有很多邊緣情況。真正的關鍵點是水平擴充套件,並建立一個解決方案,當伺服器被克隆第二次,第三次和第 n 次時。
讓我們從基本的伺服器/客戶端上傳模型開始。我們首先將檔案輸入元素新增到文件物件模型中。
<template name="example">
<input type=file />
</template>
然後將一個事件附加到控制器中的 input 元素,並呼叫本地 Meteor 方法``startFileTransfer’‘來啟動傳輸。
// 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);
}
然後,客戶端將呼叫 saveFileToDisk 伺服器方法,該方法執行實際傳輸並將所有內容放入磁碟。
//
/**
* 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,'');
}
}
});
這是一種簡單的方法,它還有很多不足之處。這可能適合上傳 CSV 檔案或其他東西,但這就是它。