9

I am new to the mean stack. I want to know how to upload an image file to the database(mongoose) through angularjs. If possible, please provide me with some code. I have searched the internet but I haven't found any suitable code.

2 Answers 2

16

You have plenty ways and tools to achieve what you want. I put one of them here:

For this one I use angular-file-upload as client side. So you need this one in your controller:

        $scope.onFileSelect = function(image) {
            if (angular.isArray(image)) {
                image = image[0];
            }

            // This is how I handle file types in client side
            if (image.type !== 'image/png' && image.type !== 'image/jpeg') {
                alert('Only PNG and JPEG are accepted.');
                return;
            }

            $scope.uploadInProgress = true;
            $scope.uploadProgress = 0;

            $scope.upload = $upload.upload({
                url: '/upload/image',
                method: 'POST',
                file: image
            }).progress(function(event) {
                $scope.uploadProgress = Math.floor(event.loaded / event.total);
                $scope.$apply();
            }).success(function(data, status, headers, config) {
                $scope.uploadInProgress = false;
                // If you need uploaded file immediately 
                $scope.uploadedImage = JSON.parse(data);      
            }).error(function(err) {
                $scope.uploadInProgress = false;
                console.log('Error uploading file: ' + err.message || err);
            });
        };

And following code in your view (I also added file type handler for modern browsers):

Upload image <input type="file" data-ng-file-select="onFileSelect($files)" accept="image/png, image/jpeg">
<span data-ng-if="uploadInProgress">Upload progress: {{ uploadProgress }}</span>
<img data-ng-src="uploadedImage" data-ng-if="uploadedImage">

For server side, I used node-multiparty.

And this is what you need in your server side route:

app.route('/upload/image')
    .post(upload.postImage);

And in server side controller:

var uuid = require('node-uuid'),
    multiparty = require('multiparty'),
    fs = require('fs');

exports.postImage = function(req, res) {
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {
        var file = files.file[0];
        var contentType = file.headers['content-type'];
        var tmpPath = file.path;
        var extIndex = tmpPath.lastIndexOf('.');
        var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
        // uuid is for generating unique filenames. 
        var fileName = uuid.v4() + extension;
        var destPath = 'path/to/where/you/want/to/store/your/files/' + fileName;

        // Server side file type checker.
        if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
            fs.unlink(tmpPath);
            return res.status(400).send('Unsupported file type.');
        }

        fs.rename(tmpPath, destPath, function(err) {
            if (err) {
                return res.status(400).send('Image is not saved:');
            }
            return res.json(destPath);
        });
    });
};

As you can see, I store uploaded files in file system, so I just used node-uuid to give them unique name. If you want to store your files directly in database, you don't need uuid, and in that case, just use Buffer data type. Also please take care of things like adding angularFileUpload to your angular module dependencies.

Sign up to request clarification or add additional context in comments.

5 Comments

I believe the require should actually be var uuid = require('node-uuid')
@Foad - thanks for this, really great. For me, this creates a file at os.tmpDir() which is then outside of the server root, so the call to fs.rename() fails because it's outside of read/write permissions. Any ideas?
@Brian You can always use __dirname to retrieve the executing directory and choose a relative path in your app's file structure.
@Foad Nosrati Habibi thank you for this solution, I am just geting used to the Mean Stack and don't know where I would be without these examples. I am looking to enhance this solution to include uploads of multiple images, if you have any information on how to do this I would really appreciate it.
In your angular, these $scope.upload = $upload.upload({ ::::::::::::::::::: to ::::::::::::::::::::: $scope.upload = Upload.upload({ The $upload doesn't work for me but Upload as document on github.com/danialfarid/ng-file-upload#node
4

I got ENOENT and EXDEV errors. After solving these, below code worked for me.

var uuid = require('node-uuid'),
multiparty = require('multiparty'),
fs = require('fs');

var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
    var file = files.file[0];
    var contentType = file.headers['content-type'];
    var tmpPath = file.path;
    var extIndex = tmpPath.lastIndexOf('.');
    var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
    // uuid is for generating unique filenames.
    var fileName = uuid.v4() + extension;
    var destPath = appRoot +'/../public/images/profile_images/' + fileName;

    // Server side file type checker.
    if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
        fs.unlink(tmpPath);
        return res.status(400).send('Unsupported file type.');
    }

    var is = fs.createReadStream(tmpPath);
    var os = fs.createWriteStream(destPath);

    if(is.pipe(os)) {
        fs.unlink(tmpPath, function (err) { //To unlink the file from temp path after copy
            if (err) {
                console.log(err);
            }
        });
        return res.json(destPath);
    }else
        return res.json('File not uploaded');
});

for variable 'appRoot' do below in express.js

path = require('path');
global.appRoot = path.resolve(__dirname);

2 Comments

I think its neccessary you include a way of creating such directory if not existing. I first get this ENOENT error before making modifications as follow var destPath = appRoot + '/public/images/profile_images/'; if (!fs.existsSync(destPath)) { fs.mkdirSync(destPath); } var destPath = destPath+fileName;

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.