Skip to content

Instantly share code, notes, and snippets.

@dariocravero
Created October 20, 2012 05:25
Show Gist options
  • Save dariocravero/3922137 to your computer and use it in GitHub Desktop.
Save dariocravero/3922137 to your computer and use it in GitHub Desktop.
Save files in Meteor

Create a Meteor app and put the client_/server_ files in a client/server directories. Also, create a public dir to save the uploaded files.

Template.example.events({
'change input': function(ev) {
_.each(ev.srcElement.files, function(file) {
Meteor.saveFile(file, file.name);
});
}
});
<template name="example">
<input type=file />
</template>
/**
* @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.saveFile = 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('saveFile', file.srcElement.result, name, path, encoding, callback);
}
fileReader[method](blob);
}
/**
* TODO support other encodings:
* http://stackoverflow.com/questions/7329128/how-to-write-binary-data-to-a-file-using-node-js
*/
Meteor.methods({
saveFile: 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,'');
}
}
});
@stegrams
Copy link

A single regex for cleaning paths and names, for both windows and *nix, could be

return str.replace(/\.\.|\/|\\/g, ''); // And yes I know! Looks like library shelf

@huevoncito
Copy link

Thanks so much for this script. I have a little problem though. I've made it so that the function loops so multiple files can be uploaded at once, by clicking a button. Thing is, when I do more than 1 file, some of the files show up as 0kb in the public folder. As empty files they're useless to me. Ideas? Thanks again!

@jnthnmyrs
Copy link

Thank you so much for this script!
I ended up needing using fs.writeFileSync instead of fs.writeFile.
For some reason, with fs.writeFile, it would only upload an empty file at first. You would then have to upload it again for it to finish the job. It was weird, but then @mike-engel suggested giving fs.writeFileSync a try and ka-bam!

@Goddard
Copy link

Goddard commented Aug 15, 2013

These does nothing. It doesn't give me an error or nothing, but I can't find the file.

@flyingfisher
Copy link

This would work after meteor update to 0.6.5 . The path is wrong.
I find a work round with process.env['PWD'].
Just put it before your path. like following:
chroot = Meteor.chroot || (process.env['PWD'] +'/public') ;

@andystiff
Copy link

Thank you so much for this, I've tried three other examples and none worked or I misunderstood them. Working beautifully.

@BruceHubbard
Copy link

I ran into problems using Meteor 6.6.3 where it wasn't saving files. Found this StackOverflow which resolved my problem (Error: ENOENT): http://stackoverflow.com/questions/18616151/fs-writefile-doesnt-work-on-meteor-0-6-5-1

Turns out you can't just use public/myFilename because Meteor changed what the root directory of your app is. When I used "../../../../../public/" it starting working properly.

@davidrinnan
Copy link

works fine! after changing to Npm.require and changing the path to reflect new location of chroot in meteor. Beware that uploading to public causes a restart to happen on the server. You should create a folder suffixed by ~ like images~ this will work without meteor restarting or updating the client.

@gabrielhpugliese
Copy link

I've uploaded the example to a repo (it's coffee, sorry):
https://github.com/gabrielhpugliese/meteor-file-upload

@tomitrescak
Copy link

Hi, thanks for the code. Worked well on localhost, but then on production server it is failing for larger files. For files cca > 2MB after some time I receive "404 Error: Page not found" (from Firebug).

Any idea?

@piyushcoader
Copy link

ev.srcElement.files doesnt works gives undefined

@stephentcannon
Copy link

@piyushcoader try ev.currentTarget.files

@raniafekry
Copy link

I tried it but it doesn't save file to public folder , I have meteor 0.9.3 , changed to
chroot = Meteor.chroot || (process.env['PWD'] +'/public') ;
and
fs = Npm.require('fs');

any solutions

@raniafekry
Copy link

@gabrielhpugliese
I tried your example meteor-file-upload got this error
Errors prevented startup:

While building package iron-dynamic-template:
error: no such package: 'blaze'

@icolR
Copy link

icolR commented Nov 7, 2014

Works for me on Meteor 1.0 replacing :
ev.srcElement.files -> ev.currentTarget.files
chroot = Meteor.chroot || 'public'; -> chroot = Meteor.chroot || (process.env['PWD'] +'/public') ;
fs = meteor_bootstrap.require('fs') -> fs = Npm.require('fs');

Thanks a lot !

@michaellouieloria
Copy link

add for meteor 1.0

Meteor.methods({
saveFile: function(blob, name, path, encoding) {
check(blob, Match.Any);
check(name, String);
check(path, String);
check(encoding, String);

@maheshbvv
Copy link

I am on meteor 1.0,

Chrome && Opera thowing an error shown below..

Uncaught TypeError: Cannot read property 'files' of undefined

When I tried on Firefox : Error is shown below

TypeError: ev.srcElement is undefined

please help

@rajeshtamire
Copy link

it worked perfectly for me...Thanks!

@typista
Copy link

typista commented Dec 25, 2014

TypeError: ev.srcElement is undefined

Works for me on Meteor 1.0.1 and 1.0.2 replacing :

client_save_file.js
30 //Meteor.call('saveFile', file.srcElement.result, name, path, encoding, callback);
31 Meteor.call('saveFile', file.target.result, name, path, encoding, callback);

@wontonst
Copy link

Can't get this to work in Meteor 1.0.3. Made fixes in comment list. Fails silently without file being written to disk. Permissions are properly set.

@anuragarya
Copy link

I am on Meteor 1.1.0.2 & I get following exception:
Exception while invoking method 'saveFile' TypeError: Object # has no method 'require'

@Cerealkillerway
Copy link

with meteor 1.1.0.2 works perfect for me, thank you;
the only thing I've changed is
fs = __meteor_bootstrap__.require('fs')
with
fs = Npm.require('fs')
inside the method

Copy link

ghost commented Nov 20, 2015

the given code is not wokring.
HTML code is written in js file
and js file is written in html file. Blunder mistake

Do you have anywhere full source code where save file is working?

Any one there?

@manusharma1
Copy link

How to change this code to fs write stream, as it is not perfect for Bigger Uploads?, Can you suggest something?

@maitrid
Copy link

maitrid commented Aug 12, 2016

After looking into different possible solutions to file uploads with Meteor 1.3 and React, I couldn't find a suitable solution. This script helped a great deal. I can upload after a few small tweaks. Of course, for Meteor 1.3 and 1.4, it works without an error after installing fs rather than using fs = Npm.require('fs'):

meteor npm install --save fs

Then, in the server_save_files.js, I added this line on the top:

import fs from 'fs';

Next thing to figure out is: How to implement a progress bar for the upload?

@saravanan4514
Copy link

Need to make the following changes to have this working in meteor 1.3

  1. import fs from 'fs'; (instead of fs = meteor_bootstrap.require('fs'))
  2. Change chroot to chroot = '../../../../../public';
  3. In helper function change the ev.srcElement.files to ev.currentTarget.files

Works like a charm after that. Thanks for the tutorial.

@varunajmera0
Copy link

code is not working. :(

@varunajmera0
Copy link

image
i am getting error that meteor_bootstrap.require('fs') is not function.

@varunajmera0
Copy link

# Thanks alot man #dariocravero

Working.
Without any package.

Thanks

@varunajmera0
Copy link

can anyone tell how to create persistence storage in development mode for meteor. Whenever server starts either remove data or give this error
Error: EBUSY: resource busy or locked, rmdir 'C:\Users\admins\Desktop\tripsy\dummy.meteor\local\build\programs\server'
at Error (native)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment