SpacePope
SpacePope

Reputation: 1423

Download files from server using Meteor.js

Here is my workflow as of now:

In a button click event, I have search results being exported to a .csv file, which is saved to the server. Once the file is saved, I want to send it for download to the browser. Using this question How to handle conditional file downloads in meteor.js, I created a method that is called after the method that saves the file returns. Here is that method:

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 )];
    });
}

My question, however, is how to invoke that route? Using .Router.to('/export?file=filename.ext') doesn't work, and causes the user to leave the current page. I want this to appear seamless to the user, and I don't want them to have any idea they are being redirected. Before anyone asks, save_path is declared outside of the method, so it does exist.

Upvotes: 4

Views: 6762

Answers (2)

SpacePope
SpacePope

Reputation: 1423

I have gotten it! However, it required the use of a few additional packages. First, let me describe the workflow a little more clearly:

A user on our site performs a search. On the subsequent search results page, a button exists that allows the user to export his/her search results to a .csv file. The file is then to be exported to the browser for download.

One concern we had was if a file is written to the server, making sure only the user who is exporting the file has the ability to view the file. To control who had visibility on files, I used a meteorite package, CollectionFS (mrt add collectionFS or clone from github). This package writes file buffers to a mongo collection. Supplying an "owner" field when saving gives you control over access.

Regardless of how the file is created, whether saved to the server via an upload form or generated on the fly the way I did using the json2csv package, the file must be streamed to CollectionFS as a buffer.

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
});

So at this point, I have taken my data file, and streamed it as a buffer into the mongo collection. Because my data exists in memory in the var csv, I stream it as a buffer by looping through each character. If this were a file saved on a physical disk, I would use fs.readFileSync(file) and send the returned buffer to CollectionFS.storeBuffer().

Now that the file is saved as a buffer in mongo with an owner, I can limit through way I publish the CollectionFS collection who can download/update/delete the file or even know the file exists.

In order to read the file from mongo and send the file to the browser for download, another Javascript library is necessary: FileSaver (github).

Using the retrieveBlob method from CollectionFS, pull your file out of mongo as a blob by supplying the _id that references the file in your mongo collection. FileSaver has a method, saveAs that accepts a blob, and exports to the browser for download as a specified file name.

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);
});

I hope someone will find this useful!

Upvotes: 6

Tarang
Tarang

Reputation: 75975

If your route works, when when your method returns you could open a new window containing the link to the text file.

You've already added in content disposition headers so the file should always ask to be saved.

Even if you just redirect to the file, because it has these content disposition headers it will ask to be saved and not interrupt your session.

Upvotes: 0

Related Questions