上传文件可以很简单也可以很复杂,这取决于您想要做什么。一般来说,传输文件本身并不是那么困难。但是在附件、二进制文件等方面有很多边缘情况。真正的症结在于水平扩展,并创建一个在第二次、第三次和第 n 次克隆服务器时有效的解决方案。
让我们从一个基本的服务器/客户端上传模型开始。我们首先向文档对象模型添加一个文件输入元素。
<template name="example"> <input type=file /> </template>
然后将事件附加到控制器中的输入元素,并调用本地 Meteor 方法“startFileTransfer”来启动传输。
// 客户端/example.js Template.example.events({ 'change input': function(ev) { _.each(ev.srcElement.files, function(file) { Meteor.startFileTransfer(file, file.name); }); } }); // 客户端/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 这需要吗?如果我们从文件上传内容,是的,但如果它来自输入/文本区域,我认为不是...... 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'; //清理路径。删除任何开头和结尾的“/”-我们为它们添加前缀-, // 任何尝试转到父目录 '..' 和任何空目录 // between '/////' - 删除 '..' 后可能会发生 path = chroot + (path ? '/' + path + '/' : '/'); // TODO 添加文件存在检查等... 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 文件或其他内容可能很好,但仅此而已。