Adding a picture to the MEAN.JS sample with Angular-file-upload

流过昼夜 提交于 2019-11-30 10:17:37

My solution uses angular-file-upload on the client and uses connect-multiparty to handle the file upload.

The images are stored directly in the database which limits their size. I have not included the required code to check the image size.

set up

 bower install ng-file-upload --save
 bower install ng-file-upload-shim --save 

 npm i connect-multiparty
 npm update

all.js add angular-file-upload scripts

            ...
            'public/lib/ng-file-upload/FileAPI.min.js', 
            'public/lib/ng-file-upload/angular-file-upload-shim.min.js',
            'public/lib/angular/angular.js', 
            'public/lib/ng-file-upload/angular-file-upload.min.js',
            ...

config.js Inject angular-file-upload dependency

...
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'angularFileUpload'];
...

article.client.controller.js Use angular-file-upload dependency

angular.module('articles').controller('ArticlesController', ['$scope', '$timeout',  '$upload', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $timeout, $upload, $stateParams, $location, Authentication, Articles) {
    $scope.fileReaderSupported = window.FileReader !== null;



    // Create new Article
            $scope.create = function(picFile) {
          console.log('create');
                      console.log(picFile);
        var article = new Articles({
            title: this.title,
            content: this.content,
            image: null
        });

         console.log(article);
         $upload.upload({
            url: '/articleupload', 
            method: 'POST', 
            headers: {'Content-Type': 'multipart/form-data'},
            fields: {article: article},
            file: picFile,               
        }).success(function (response, status) {
              $location.path('articles/' + response._id);

            $scope.title = '';
            $scope.content = '';
        }).error(function (err) {
                $scope.error = err.data.message;
        });

    };

    $scope.doTimeout = function(file) {
         console.log('do timeout');
        $timeout( function() {
                var fileReader = new FileReader();
                fileReader.readAsDataURL(file);
             console.log('read');
                fileReader.onload = function(e) {
                    $timeout(function() {
                        file.dataUrl = e.target.result;
                         console.log('set url');
                    });
                };
            });
    };


    $scope.generateThumb = function(file) {
        console.log('generate Thumb');
    if (file) {
        console.log('not null');
         console.log(file);
        if ($scope.fileReaderSupported && file.type.indexOf('image') > -1) {
            $scope.doTimeout(file);
          }
      }
   };
}

create-article.client.view.html update the create view to handle file selection and upload

<section data-ng-controller="ArticlesController">
<div class="page-header">
    <h1>New Article</h1>
</div>
<div class="col-md-12">
    <form name="articleForm" class="form-horizontal" data-ng-submit="create(picFile)" novalidate>
        <fieldset>
            <div class="form-group" ng-class="{ 'has-error':   articleForm.title.$dirty && articleForm.title.$invalid }">
                <label class="control-label" for="title">Title</label>
                <div class="controls">
                    <input name="title" type="text" data-ng-model="title" id="title" class="form-control" placeholder="Title" required>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label" for="content">Content</label>
                <div class="controls">
                    <textarea name="content" data-ng-model="content" id="content" class="form-control" cols="30" rows="10" placeholder="Content"></textarea>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label" for="articleimage">Article Picture</label>
                <div class="controls">
                     <input id="articleimage" type="file" ng-file-select="" ng-model="picFile" name="file" accept="image/*" ng-file-change="generateThumb(picFile[0], $files)" required="">
                    <br/>
                    <img ng-show="picFile[0].dataUrl != null" ng-src="{{picFile[0].dataUrl}}" class="img-thumbnail" height="50" width="100">
                    <span class="progress" ng-show="picFile[0].progress >= 0">      
                        <div style="width:{{picFile[0].progress}}%" ng-bind="picFile[0].progress + '%'" class="ng-binding"></div>
                    </span> 
                    <span ng-show="picFile[0].result">Upload Successful</span>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-default" ng-disabled="!articleForm.$valid" ng-click="uploadPic(picFile)">
            </div> 
            <div data-ng-show="error" class="text-danger">
                <strong data-ng-bind="error"></strong>
            </div>
        </fieldset>
    </form>
</div>
</section>

view-article.client.view.html update list view to include image

<img ng-src="data:image/jpeg;base64,{{article.image}}" id="photo-id" width="200" height="200"/>

list-articles.client.view.html update view to include imgae

<img ng-src="data:image/jpeg;base64,{{article.image}}" id="photo-id" width="40" height="40"/>

article.server.model.js Add image to database model

...
image: {
    type: String,
    default: ''
},
...

article.server.routes.js Add new route for upload use connect-multiparty

...
multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty(),
...
app.route('/articleupload')
    .post(users.requiresLogin, multipartyMiddleware, articles.createWithUpload);
...

article.server.controller.js Handle new route for upload require fs

...
fs = require('fs'),
...
/**
 * Create a article with Upload
 */
exports.createWithUpload = function(req, res) {
 var file = req.files.file;
 console.log(file.name);
 console.log(file.type);
 console.log(file.path);
 console.log(req.body.article);

var art = JSON.parse(req.body.article);
var article = new Article(art);
article.user = req.user;

fs.readFile(file.path, function (err,original_data) {
 if (err) {
      return res.status(400).send({
            message: errorHandler.getErrorMessage(err)
        });
  } 
    // save image in db as base64 encoded - this limits the image size
    // to there should be size checks here and in client
  var base64Image = original_data.toString('base64');
  fs.unlink(file.path, function (err) {
      if (err)
      { 
          console.log('failed to delete ' + file.path);
      }
      else{
        console.log('successfully deleted ' + file.path);
      }
  });
  article.image = base64Image;

  article.save(function(err) {
    if (err) {
        return res.status(400).send({
            message: errorHandler.getErrorMessage(err)
        });
    } else {
        res.json(article);
    }
  });
});
};
...

Thanks Charlie Tupman for this last solution which works very well.

I simply add the dependencies :

...
multiparty = require('multiparty'),
uuid = require('uuid'),
...

before the exports function and changed two lines to improve the behavior:

...
var destPath = './public/uploads/' + fileName;

article.image = '/uploads/' + fileName;
...

which resolve in:

exports.createWithUpload = function(req, res) {

    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {

        var file = req.files.file;
        console.log(file.name);
        console.log(file.type);
        console.log(file.path);
        console.log(req.body.article);

        var art = JSON.parse(req.body.article);
        var article = new Article(art);
        article.user = req.user;
        var tmpPath = file.path;
        var extIndex = tmpPath.lastIndexOf('.');
        var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
        var fileName = uuid.v4() + extension;
        var destPath = './public/uploads/' + fileName;

        article.image = '/uploads/' + fileName;

        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);
                }
            });
            article.save(function(err) {
                if (err) {
                    return res.status(400).send({
                        message: errorHandler.getErrorMessage(err)
                    });
                } else {
                    res.jsonp(article);
                }
            });
        } else
            return res.json('File not uploaded');
    });

};

My working solution in MEAN.js

server model:

image:{ 
    type: String, 
    default: ''
},

server controller:

var item = new Item(JSON.parse(req.body.item));
item.user = req.user;
if(req.files.file)
    item.image=req.files.file.name;
else
    item.image='default.jpg';

//item.image=
item.save(function(err) {
    if (err) {
        return res.status(400).send({
            message: errorHandler.getErrorMessage(err)
        });
    } else {
        res.jsonp(item);
    }
});

server route: (requires multer: "npm install multer --save")

var multer  = require('multer');
app.use(multer({ dest: './public/uploads/'}));

frontend angular controller:

    $scope.image='';

    $scope.uploadImage = function(e){
        console.log(e.target.files[0]);
        $scope.image=e.target.files[0];

    };



    // Create new Item
    $scope.create = function() {
        // Create new Item object
        var item = new Items ({
            name: this.name,
            bought: this.bought,
            number: this.number,
            description: this.description,
            warranty: this.warranty,
            notes: this.notes


        });

        ItemsService.saveItem(item,$scope.image);

    };

service which send's the request:

.factory('ItemsService', ['$http','$rootScope', function($http, $rootScope) 
{
    var service={};

    service.saveItem = function(item, image)
    {

        var fd = new FormData();
        fd.append('file', image);
        fd.append('item', JSON.stringify(item));
        $http.post('items/', fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .success(function(){
            console.log('success add new item');
        })
        .error(function(e){
            console.log('error add new item', e);
        });


    };

    return service;

}

]);

html view:

           <div class="form-group">
                <label class="control-label" for="name">Image</label>
                <div class="controls">
                    <input type="file"  data-ng-model="image" id="image" my-file-upload="uploadImage" required>
                     {{selectedFile.name}}
                </div>
            </div>

@john prunell Thank you so much for this, I finally figured it out (took me over a week but i am now much more comfortable with the stack) I have forked it to get it to upload the file to a folder:

'exports.createWithUpload =     function(req, res) {

var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {

 var file = req.files.file;
 console.log(file.name);
 console.log(file.type);
 console.log(file.path);
 console.log(req.body.article);

var art = JSON.parse(req.body.article);
var article = new Article(art);
article.user = req.user;
var tmpPath = file.path;
var extIndex = tmpPath.lastIndexOf('.');
var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
var fileName = uuid.v4() + extension;
var destPath = './uploads/' + fileName;

article.image = fileName;

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);
        }
    });
    article.save(function(err) {
    if (err) {
        return res.status(400).send({
            message: errorHandler.getErrorMessage(err)
        });
    } else {
        res.jsonp(article);
    }
});
}else
    return res.json('File not uploaded');
});

};

What I would like to do now is fork your solution to allow uploading of multiple images in the same way, if you have any insight into how to do this that would be amazing, I will let you know how I get on.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!