使用MEAN.js上传照片

时间:2016-05-13 14:22:56

标签: javascript angularjs node.js mongodb ng-file-upload

我正在使用基于MEAN堆栈的this tutorial。 我想存储用户可以在服务器上使用MongoDB上传的照片。

包括:Angular directive to upload files

创建-spot.client.view.html

<div data-ng-controller="SpotsCreateController">
    <form class="form-signin" data-ng-submit="create(picFile)" novalidate>
        <label>Upload an image</label>
        <input type="file" id="articleimage" ng-model="picFile" ng-file-select="" ng-file-change="generateThumb(picFile[0], $files)" multiple name="file" accept="image/*">
        <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>

        <input type="submit" class="btn btn-lg btn-primary btn-block" ng-click="uploadPic(picFile)">

        <div data-ng-show="error">
            <strong data-ng-bind="error"></strong>
        </div>
    </form>
</div>

查看-spot.client.view.html

<div data-ng-controller="SpotsViewController">
    <section data-ng-init="findOne()">
        <img ng-src="data:image/jpeg;base64,{{spot.image}}" id="image-id" width="200" height="200"/>
    </section>
</div>

的application.js

var app = angular.module('newApp', ['ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch', 'ui.bootstrap', 'users', 'spots']);

spots.create.client.controller.js

angular.module('spots').controller('SpotsCreateController', ['$scope', '$timeout', 'Authentication', 'Spots', '$location'
function($scope, $timeout, Authentication, Spots, $location) {
    $scope.authentication = Authentication;
    $scope.fileReaderSupported = window.FileReader !== null;

    $scope.create = function(picFile) {
        var spot = new Spots({
            title: this.title,
            description: this.description,
            image: null
        });
        spot.$save(function(response) {
            $location.path('spots/' + response._id);
        }, function(errorResponse) {
            $scope.error = errorResponse.data.message;
        });
    };

    $scope.doTimeout = function(file) {
        $timeout( function() {
            var fileReader = new FileReader();
            fileReader.readAsDataURL(file);
            fileReader.onload = function(e) {
                $timeout(function() {
                    file.dataUrl = e.target.result;
                });
            };
        });
    };
    $scope.generateThumb = function(file) {
        if (file) {
            if ($scope.fileReaderSupported && file.type.indexOf('image') > -1) {
                $scope.doTimeout(file);
            }
        }
    };

spot.server.model.js

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var SpotSchema = new Schema({
    ...
    image: {
        type: String,
        default: '',
        required: false
    }
});

mongoose.model('Spot', SpotSchema);

spots.server.routes.js

var multiparty = require('connect-multiparty'),
    multipartyMiddleware = multiparty();

module.exports = function(app) {
    app.route('/api/spots')
        .get(spots.list)
        .post(users.requiresLogin, multipartyMiddleware, spots.create);

    app.route('/api/spots/:spotId')
        .get(spots.read)
        .put(users.requiresLogin, spots.hasAuthorization, spots.update)
        .delete(users.requiresLogin, spots.hasAuthorization, spots.delete);

    app.param('spotId', spots.spotByID);
};

spots.server.controller.js

var mongoose = require('mongoose'),
    fs = require('fs'),
    Spot = mongoose.model('Spot');

exports.create = function(req, res) {
    if (req.files.file) {
        var file = req.files.file;
    }

    var spot = new Spot(req.body);
    spot.creator = req.user;

    fs.readFile(file.path, function (err,original_data) {
        if (err) {
            return res.status(400).send({
                message: getErrorMessage(err)
            });
        }
        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);
            }
        });
        spot.image = base64Image;
        spot.save(function(err) {
            if (err) {
                return res.status(400).send({
                    message: getErrorMessage(err)
                });
            } else {
                res.json(spot);
            }
        });
    });
};

我做错了什么?请考虑我要限制文件大小,我认为使用base64是一个好的开始。问题是照片没有存储在数据库中,因为控制器不能与其余部分一起使用。

2 个答案:

答案 0 :(得分:2)

您遇到的问题究竟是什么,以及出现了什么问题?

对于Express后端应用程序,我通常使用multer中间件来处理文件上传。此外,我创建了单独的路由/控制器来处理文件,而不是在我保存父对象的同时尝试处理它们。这允许我很好地分离逻辑,而不用担心在文件上载失败时没有保存父对象。你可以使用JS API进行ng-file-upload来处理Angular。

Express中的示例路线(我们有一个&#34;俱乐部&#34;带有&#34;徽标&#34;图像在这里):

router.post(
  '/logo',
  ensureAuthenticated, ensureAdmin,
  logoCtrl.upload,
  logoCtrl.save
);
router.get(
  '/:clubId/logo.*',
  logoCtrl.stream
);

控制器方法示例:

let multer = require('multer');
module.exports = {

  /**
   * Upload logo
   */
  save(req, res, next) {

    //Get club and file
    let club = req.user.club;
    let file = req.file;

    //Update
    club.logo = {
      data: file.buffer,
      mimeType: file.mimetype
    };

    //Save
    club.save()
      .then(() => {
        res.end();
      })
      .catch(next);
  },

  /**
   * Stream logo
   */
  stream(req, res, next) {
    let club = req.club;
    res.contentType(club.logo.mimeType);
    res.send(club.logo.data);
  },

  /**
   * Upload middleware
   */
  upload(req, res, next) {

    //Create upload middleware
    let upload = multer({
      storage: multer.memoryStorage(),
      limits: {
        fileSize: 50000000
      }
    }).single('logo');

    //Use middleware
    upload(req, res, next);
  }
};

正如您所看到的,multer非常简单,您真正需要的只是上传徽标的一条路线,有两种控制器方法,一种是注册multer中间件并处理文件,另一种是将它保存到MongoDB(在这种情况下,在请求中附加到俱乐部)。

确保ng-file-upload使用相同的字段名称来上传multer所期望的文件。在上面的示例中,&#34; logo&#34;。如果您不确定,请检查您的客户端应用程序发送给服务器的请求,并确保服务器应用程序需要相同的字段名称。

如果您有进一步的麻烦,请告诉我。

答案 1 :(得分:1)

您可以使用formidablegridfs-stream

//controller
var mongoose = require('mongoose'),
  fs = require('fs'),
  Spot = mongoose.model('Spot');

exports.create = function(req, res) {
  handleRequest(req, function(err, spot) {
    if(err) {
      return res.status(400).send({
        message: getErrorMessage(err)
      });
    }

    res.json(spot);

  });
};

function handleRequest(req) {

  var spot = new Spot(req.body);
  spot.creator = req.user;

  var formidable = require('formidable');

  var form = new formidable.IncomingForm();

  form.parse(req, function(err, fields, files) {
    if (err) {
      return done(err);
    }
    var file = files.qqfile;
    if (file && file.name && file.name.trim !== '') {
      if (file.size > 5000000) {
        message = 'file is too large';
      }
      if (!file.type) {
        message = 'file is not an image';
      }
      if (file.type.indexOf('image/') !== 0) {
        message = 'file is not an image type';
      }
    }
    if (message) {
      logger.info('Uploading failed', file, message);
      return done(message);
    }

    uploadFile(mongoose.connection, 'Pictures', files.qqfile, require('uuid').v1(), function(err) {
      if (err) {
        return done(err);
      }
      if (!data) return done(false, null);

      if (typeof data === 'string') {
        data = JSON.parse(data);
      }
      logger.info('[PHOTOS]', 'Uploaded', data.filename);
      photo = {
        unique_id: token,
        name: file.name,
        contentType: file.type
      };

      spot.photos = spot.photos || [];
      spot.photos.push(photo);
      spot.markModified('photos');
      spot.save(done);

    });
  });
}

function uploadFile(DB, className, data, token, callback) {
  var grid = require('gridfs-stream');
  var gfs = grid(DB.db, mongoose.mongo);

  var writestream = gfs.createWriteStream({
    filename: token,
    root: className
  });
  writestream.on('close', function (file) {
    return callback(null, file);
  });

  if (data.path) {
    var fs = require('fs');
    if (!fs.existsSync(data.path)) {
      return callback(false);
    }
    var pipe = false;
    if (data.pipe) {
      pipe = data.pipe;
    } else {
      var fs = require('fs');
      if (!fs.existsSync(data.path)) {
        return callback(false);
      }
      var rs = fs.createReadStream(data.path);
      pipe = rs.pipe.bind(rs);
    }
    return pipe(writestream);
  } else {
    logger.error('[PHOTO] no path', data);
  }
  callback(false);
}