4

这是我现在的工作流程:

在按钮单击事件中,我将搜索结果导出到 .csv 文件,该文件保存到服务器。保存文件后,我想将其发送到浏览器下载。使用这个问题How to handle conditional file downloads in meteor.js,我创建了一个在保存文件的方法返回后调用的方法。这是那个方法:

exportFiles: function(file_to_export) {
    console.log("to export = "+file_to_export);
    Meteor.Router.add('/export', 'GET', function() {
        console.log('send '+file_to_export+' to browser');
        return [200,
        {
           'Content-type': 'text/plain',
           'Content-Disposition': "attachment; filename=" + this.request.query.file
        }, fs.readFileSync( save_path + this.request.query.file )];
    });
}

但是,我的问题是如何调用该路由?使用.Router.to('/export?file=filename.ext')不起作用,并导致用户离开当前页面。我希望这对用户来说是无缝的,我不希望他们知道他们正在被重定向。在有人问之前,save_path在方法之外声明了,所以它确实存在。

4

2 回答 2

6

我已经明白了!但是,它需要使用一些额外的软件包。首先,让我更清楚地描述一下工作流程:

我们网站上的用户执行搜索。在随后的搜索结果页面上,存在一个按钮,允许用户将他/她的搜索结果导出到 .csv 文件。然后将该文件导出到浏览器以供下载。

我们担心的一个问题是,如果将文件写入服务器,确保只有导出文件的用户才能查看文件。为了控制谁可以查看文件,我使用了一个陨石包 CollectionFS(mrt add collectionFS or clone from github)。此包将文件缓冲区写入 mongo 集合。保存时提供“所有者”字段可让您控制访问。

无论文件是如何创建的,无论是通过上传表单保存到服务器还是像我使用 json2csv 包那样动态生成,文件都必须作为缓冲区流式传输到 CollectionFS。

var userId = Meteor.userId()
var buffer = Buffer(csv.length);  //csv is a var holding the data for write
var filename = "name_of_file.csv";
for ( var i=0; i<csv.length; i++ ) {
  buffer[i] = csv.charCodeAt(i);
}
CollectionFS.storeBuffer(filename, buffer, {
    contentType: 'text/plain',
    owner: userId
});

所以此时,我已经获取了我的数据文件,并将其作为缓冲区流式传输到 mongo 集合中。因为我的数据存在于 var csv 的内存中,所以我通过循环遍历每个字符将其作为缓冲区流式传输。如果这是保存在物理磁盘上的文件,我会使用 fs.readFileSync(file) 并将返回的缓冲区发送到 CollectionFS.storeBuffer()。

现在该文件已保存为 mongo 中的缓冲区并拥有所有者,我可以通过发布 CollectionFS 集合的方式来限制谁可以下载/更新/删除文件,甚至知道该文件存在。

为了从 mongo 读取文件并将文件发送到浏览器进行下载,需要另一个 Javascript 库:FileSaver ( github )。

使用 CollectionFS 中的 retrieveBlob 方法,通过提供引用 mongo 集合中文件的 _id 将文件作为 blob 从 mongo 中拉出。FileSaver 有一个方法 saveAs,它接受一个 blob,并以指定的文件名导出到浏览器以供下载。

var file = // file object stored in meteor
CollectionFS.retrieveBlob(file._id, function(fileItem) {
    if ( fileItem.blob ) saveAs(fileItem.blob, file.filename);
    else if ( fileItem.file ) saveAs(fileItem.file, file.filename);
});

我希望有人会觉得这很有用!

于 2013-09-06T20:22:11.203 回答
0

如果您的路线有效,当您的方法返回时,您可以打开一个包含文本文件链接的新窗口。

您已经添加了内容处置标头,因此文件应始终要求保存。

即使您只是重定向到文件,因为它具有这些内容处置标头,它也会要求保存并且不会中断您的会话。

于 2013-08-28T08:41:53.673 回答