Is there a simple way to upload image?

Hi,

I would like to allow my admin to upload images on the website.

I found lot of packages on internet but they seems all very complicated for what i want.

In fact i just have a backoffice and this will be the only place where users can upload image, so i am searching for an easy way to just upload file, store it in a server directory and just get the url to store it in my databases.

All solution i found, are way more sophisticated than this, and i can’t figure out how to make them works.

Please tell me there is just a simple way to do this ^^

Thanks for your time !

Super simple to set up and works like a charm

Unless you are planning to host the application yourself I’d avoid storing locally. Many container-based deployments (including Galaxy) don’t provide permanent local storage. When containers are scaled out/in or migrated, any local storage is lost. Definitely go with cloud storage.

Hi guys, thanks for your awnsers.

I don’t understand why i cant store image locally ?

I plan to deploy the app on a server i own and i will have total control on it so what’s the problem.

Plus i definetly dont want to use cloud storage for many reasons (including client request) so tell me there is a solution for me :smiley:

Yes, there is. It’s a two-pronged approach - firstly, you don’t want to have your app reload every time you upload something (which means /public is right out), secondly, you need to know how to upload files through server code.

The first aspect is easily solved - you simply use nginx as a proxy server in front of meteor. This has several advantages:

  • you can easily enable SSL
  • gzip and other features are enabled as well
  • you can serve static files directly without involving node / meteor.

This is my nginx configuration:

upstream my_meteor_app {
        server 127.0.0.1:3000;
}

server {
        listen [::]:80;
        listen 80;
        server_name your_dns_address.here;
        access_log /var/log/nginx/your_dns_address.here.ssl.access_log;
        error_log /var/log/nginx/your_dns_address.here.ssl.error_log;

        location /static {
                alias /var/www/static;
        }
        location / {
                proxy_pass http://my_meteor_app/;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

This means that everything except /static is served by meteor while your uploads will live in /var/www/static

The meteor code to accomplish this looks like this:

On the client, somewhere in a template, React module or whatever (this example uses React but you can easily change that)

<input type="file" onChange={(event) => { beginFileUpload(event); }} />

and the corresponding code for the function beginFileUpload

beginFileUpload: (e) => {
            let input = e.target;
            _.each(input.files, function(file) {
                let fileReader = new FileReader();
                let encoding = "binary";
                let name = file.name;
                fileReader.onload = function() {
                    if(fileReader.readAsBinaryString) {
                        Meteor.call('saveFile', fileReader.result, name, '', encoding);
                    } else {
                        var binary = "";
                        var bytes = new Uint8Array(fileReader.result);
                        var length = bytes.byteLength;
                        for(var i=0; i < length; i++) {
                            binary += String.fromCharCode(bytes[i]);
                        }
                        Meteor.call('saveFile', binary, name, '', encoding);
                    }
                };
                fileReader.onloadend = function (e) {
                    console.log(e);
                };
                fileReader.onloadstart = function (e) {
                    console.log(e);
                };
                fileReader.onprogress = function (e) {
                    console.log(e);
                };
                fileReader.onabort = function (e) {
                    console.log(e);
                };
                fileReader.onerror = function (e) {
                    console.log(e);
                };
                if(fileReader.readAsBinaryString) {
                    fileReader.readAsBinaryString(file);
                } else {
                    fileReader.readAsArrayBuffer(file);
                }
            });
        }

The if(fileReader.readAsBinaryString) is necessary only if you want to support InternetExplorer because IE does not have this method. You can see that you can easily plugin other functions to update the user about the progress of the upload.

Should also work for multiple files.

With this code you’re calling a meteor method. It looks like this:

const fs = require('fs');
const pathNpm = require('path');

Meteor.methods({

    saveFile: function(blob, name, path, encoding) {
        if(Roles.userIsInRole(Meteor.user()._id, 'admin', 'group_name')) {
            if(process.env.NODE_ENV === "production") {
                var path = '/var/www/static';
            } else {
                var path = process.env['METEOR_SHELL_DIR'] + '/../../../public/static';
            }
            var filename = name.toLowerCase().replace(/ /g,'_').replace(/ä/g,'ae').replace(/ö/g,'oe').replace(/ü/g,'ue').replace(/ß/g,'ss').replace(/[^a-z0-9_.]/g,'');
             fs.writeFile(path+"/"+filename, blob, encoding, Meteor.bindEnvironment(function(err){
                if(err) {
                    console.log("Error:"+err);
                } else {
                    console.log("Success");
                     WhateverCollectionYouNeed.insert({name: name, filename: filename});
                }
            }));
        }
    }
})

This method does a bit more than strictly needed but you get the gist - three things to mention, though:
a) this one here uses alanning:roles to determine if a user is allowed to actually save the file (in this case, only the role “admin” for the group “group_name” would be allowed to do so).
b) The if(process.env.NODE_ENV === "production") is a workaround for Windows because it’s a bit complicated to determine where the development directory lives exactly. In this case, if we’re in development files will be saved to /public/static in your meteor directory (you’ll have to create this directory yourself, though).
c) I’m also doing a regex to replace special characters (the German Umlauts get translated to their ASCII equivalents, everything else special gets simply deleted, leaving only ASCII).

Oh, and don’t forget to give your web server r/w rights to /var/www/static

4 Likes

I use this package https://github.com/Lepozepo/cloudinary for cloudinary.

Erm. (20 chars limit, blah)

I use https://www.filestack.com/ it has a lot of options and you don’t have to worry about storage if you don’t want to. But it is a service, so once you scale you have to pay for it.

Guys. Again: He does not want to do cloud storage.

1 Like

There are a couple reasons why you’re getting cloud storage:

  1. People just are not reading the question fully.
  2. Meteor just doesn’t do file storage gracefully and is generally more complicated then any of the API solutions available. A couple of the best Meteor file storage packages are no longer maintained by the owners has also contributed to people going the cloud storage route.

I would almost question what is wrong with wanting to store locally. What development value are you looking for that leads you to this decision?

I know with Cloudinary, you get a lot in return as far as image tracking, storage the ability to manipulate the images much like Drupals image style module. All of which would be annoying to build in Meteor every time I want to do a build.

Sometimes you want to upload files which are not images. For example, I’m uploading files from the SMARTBoard software which are basically zipped SVGs - so I have to unzip and render them to PDF using a headless Inkscape installation.

Just one example.

Is this one of those cases, or is it just a simple image upload as the title state?

He said that his client didn’t want to use the cloud. That pretty much ends the discussion about that aspect :slight_smile:

It is till important to ask why though, no reason to reinvent the wheel when there is already great tools out there for handling this.

But as I said above most people probably just saw the title and did not read the body of the post. I myself am guilty of this. But after thinking about it the “why not” crossed my mind.

I am curious to what solution OP used.

1 Like

Hi guys,

Thanks alot for all your awnsers,
I will try to make them works :wink:

About why i can’t use cloud storage, for images it would be ok, but later on the developement my client will upload files that have security issues. They don’t want those files on an other server, thats it :wink:

Thanks alot, your awnser is really helpfull.
About nginx, this is a solution i can use, but i would like to know, isn’t a way to say to meteor that he dont need to reload when /public/static is modified ? Seems like a basic configuration …

EDIT : found it ; i should use meteor -once to prevent reloading on changement. But also its say isn’t a good idea to allow users store data in /public, is it a real problem if the only user allow to do that is the admin ?
(source : http://stackoverflow.com/questions/32684792/meteor-dont-reload-page-when-some-folder-changes)

This is only relevant during development (testing) and actually terminates the Meteor process after running.

When you deploy a Meteor application into production there is no meteor command and you can change files without affecting the running application.