Recommended way to upload images in meteor: No plugins, no external services (Solved)


#1

Is there an official way of uploading images in Meteor?

A few packages promise to do so, but I’m not a fan of packages as they could get deprecated, or be unnecessarily bloated.

I read about collectionFS, but it doesn’t seem to be maintained anymore, I also want to upload to my own server.


#2

What about UploadFS: https://github.com/jalik/jalik-ufs ?


#3

Hello Myon,

I don’t want to deal with packages if possible, you never know when it will stop being maintained. I was hoping there was a native way, or some code I could hook into.

Thank you.


#4

I am going through this right now, and have my file’s working with

You need to configure your server.js and do some client stuff to make it work. It’s rather complicated, but satisfying once it works.

Now, be aware on your production, things get very messy again. If you self host, it’s not so bad, just some messing around and reconfiguring paths.

If you’re going to use Meteor Galaxy, which I totally recommend, you need to get hooked up with Amazon S3 storage, which can be found here:

Book at least 1 solid week trying to understand how to do this. Good luck!


#5

If you don’t want to use packages, you could just use the Amazon API for example.


#6

Thank you all, but no external servers, and I want to host myself. Will hack something together, and post the results here.


#7

Again, you can use that Meteor-Files package. It totally works for self hosting.

But let me tell you man, after MONTHS of pulling my hair out, I moved to Galaxy. Within the hour, I was up and running, it’s BEAUTIFUL. I pulled the plug on my Ubuntu server and haven’t looked back since. You have to be an apache/nginx PRO to get self hosting working, solid knowledge of DNS, port, and socket management is absoultely required.

Feel free to check out how wicked www.SkyRooms.IO is running now. I’m about to push an update with the Amazon File Storage working, but currently it’s using just /tmp hosting, which resets to an empty directory after an update goes live (which is why Galaxy tells you to get Amazon S3).

I wasted so much time. lol


#8

I’m currently working on cloud9, I try not to use modules unless I have no choice (bad experiences coming from PHP:WordPress)

I checked out SkyRooms, and sleek is the word that comes to mind, very clean UI.


#9

Thank you! I’m actually a full time PHP and Wordpress developer. I have to say, make sure you follow the post / postmeta database model in to your collections, WAY easier to work with in this schema.

I hear you about modules. I built SkyRooms on Wordpress with open source plugins and had to trash it, was so slow even with CDN and caching. Total garbage.


#10

Hello,

Finally got something working:

//server.js

//check if user is actually user
Meteor.methods({
    checkuploaduser: function (userId) {
        if (userId !=null && this.userId == userId) {
            valid = true;
            picid = userId
        }
    }
});

//upload stuff
WebApp.connectHandlers.use('/file', (req, res) => {
if (valid) {
res.setHeader("Access-Control-Allow-Methods", "PUT");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (req.method === 'OPTIONS') {
    res.writeHead(200);
    res.end();
    return;
    } else if (req.method === 'PUT') {
    if (!req.headers['content-type'].startsWith('image')) {
        res.writeHead(400);
        res.end();
    }
    let getFile = Meteor.wrapAsync(done => {
        let chunks = [];
        var chunk = null;
        while (null !== (chunk = req.read())) chunks.push(chunk);
        // req.on('readable', () => {
        //     chunks.push(req.read());
        // });
        //console.log(chunks)
        
        req.on('end', () => {
            done(undefined, Buffer.concat(chunks));
        });
    });
    let buffer = getFile();
    //console.log(buffer)
	//encode buffer as base64
    var base64data = new Buffer(buffer).toString('base64');
    //console.log(picid);
	//base64 as image in mongo
    Meteor.users.update({_id: picid}, {$set: {"profile.image": base64data}});
	//the comments below are for saving to local dir if you want
		//let path = process.env['METEOR_SHELL_DIR'] + '/../../../public';
		// const fs0 = require('fs');
		// filename = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
		// fs0.writeFile(path + "/" + filename, buffer, Meteor.bindEnvironment(function (err) {
			// if (err) {
				// console.log("Error:" + err);
			// } else {
				// console.log("Success");
			// }
		// }));
    res.writeHead(200);
    res.end();
} }
});
//client.js
Template.changeimage.events({
  'change input[type="file"]': function (event) {
    var file = event.target.files[0]
    console.log(file)
    var xhr = new XMLHttpRequest()
    userId=Meteor.userId()
    xhr.open('PUT', 'https://yourapp.com/file', true)
       xhr.onload = (event) => {
          console.log('done uploading!');
        };
    xhr.upload.onprogress = (event) => {
      let percent = 100 * (event.loaded / event.total);
      console.log(percent+'% uploaded');
    };
    Meteor.call('checkuploaduser', Meteor.userId());
    xhr.send(file);
  }
})
//template html
<template name="changeimage">
<input type="file">
</template>

Got a lot of help from this link:
https://iamlawrence.me/uploading-files-with-meteor

Its a bit buggy, I’m sure someone more experienced can make it better, but does the job for me.


#11

Oh hey very cool!

So your code is pushing an uploaded file out to a receiving server for storage? Very cool.

I’m having an issue with image compression / optimization on S3 and CloudFront.


#12

I do not agree, at least not with with Meteor and an EC2 instance (Ubuntu) and I don’t think it applies to any other Meteor + cloud hosting solution either. :smile:


#13

I am definitely not that kind of expert, so yes.


#14

I think it would be better to create a new question, and send me the link.


#15

Even tho I’m a bit late to the party I thought I’d give you a few quick suggestions,

  1. Check so that the upload is smaller than 16MB, MongoDB won’t like you otherwise :slight_smile:

  2. Try not to use the profile field, it might be tempting but it is not advised (instead of listing the reasons I’ll point ya’ to this part of the guide: https://guide.meteor.com/accounts.html#dont-use-profile )

  3. Maybe try something like minio, it is at least self-hosted. The nice thing about it is that you can generate a “pre-signed” URL to which the client can upload a file. It is also a lot more expensive and slower to host blobs on a database, it isn’t made for that specific application whilst the filesystem is perfect for the job! You can also easily switch to S3 or any other S3 compatible hosting if you so wish!
    I’d suggest to just upload the profile image (in this case) with the same ID as the user, then you can easily just get it from the bucket at any time, you could also do cool stuff such as adding several images of different sizes by just appending the size to the end of the filename and gain some performance.

(tl;dr, minio is like S3 (and compatible with it), self-hosted and is an extremely active project, use it to upload files to a proper fast filesystem!)


#16

Hello Novium,

Thank you for the suggestion. I started writing python handle the uploads to run on server, move outside app directory to prevent refreshes, and return a link. I’ll look into minio.

In anycase, I am done with the project, and on to something more serious. I decided to create it because I wanted to learn Meteor. This will come in handy for important projects.


#17

Reviving an old thread here, but hoping someone can help.

In the code above, @crane3700 (who I suspect is no longer around here) uses a Method to validate the user by setting ‘valid’ to ‘true’ inside the Method if the user is logged in.

I don’t understand how this ‘valid’ variable can be used outside of the method, and especially how it can be used for the specific user who’s logged in.

When I try to do this I just get ‘valid’ is undefined. Or if I set it to false in a high scope it just stays as false.

Is there a way to do this, or is this a dead-end?


#18

Hello gazhayes,

This user is still here.

I don’t remember what of much I did with that code, but it looks like I
passed the userId when calling the function, or I had defined it in an
external file which auto appends.

Can you provide more information as to what you want to do, so I can
provide a clear answer?


#19

Is it still running?


#20

Check so that the upload is smaller than 16MB, MongoDB won’t like you otherwise :slight_smile:

  1. GridFS can easily solve this.