简体   繁体   English

使用MEAN.js上传照片

[英]Uploading photographs using MEAN.js

I'm using this tutorial , which is based on the MEAN stack. 我正在使用本教程该教程基于MEAN堆栈。 I want to store photographs that users can upload with MongoDB on a server. 我想将用户可以通过MongoDB上传的照片存储在服务器上。

included: Angular directive to upload files 包括: Angular指令上传文件

create-spot.client.view.html 创建-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>

view-spot.client.view.html 视图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 的application.js

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

spots.create.client.controller.js 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 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 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 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);
            }
        });
    });
};

What did I do wrong? 我做错了什么? Please consider that I want to limit the file sizes, and I thought using base64 is good start. 请考虑我想限制文件大小,并且我认为使用base64是一个好的开始。 The issue is that the photograph is not stored in the database because the controller doesn't work with the rest. 问题是照片未存储在数据库中,因为控制器无法与其他照片一起使用。

What exactly is the problem you are experiencing, and at what step is it going wrong? 您遇到的问题到底是什么?错在哪一步?

For Express backend apps I usually use multer middleware for handling file uploads. 对于Express后端应用程序,我通常使用multer中间件来处理文件上传。 Also, I create separate routes/controllers for dealing with the files rather than trying to process them at the same time I'm saving the parent object. 另外,我创建了单独的路由/控制器来处理文件,而不是在保存父对象的同时尝试处理它们。 This allows me to separate the logic nicely and not worry about the parent object not being saved when the file upload fails. 这使我可以很好地分离逻辑,而不必担心文件上传失败时不会保存父对象。 You could use the JS API for ng-file-upload to handle that in Angular. 您可以使用ng-file-upload的JS API在Angular中进行处理。

Example routes in Express (we have a "club" with a "logo" image here): Express中的路线示例(此处有一个带有“徽标”图像的“俱乐部”):

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

Example controller methods: 控制器方法示例:

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);
  }
};

So as you can see, it's quite simple with multer and all you really need is one route for uploading the logo, with two controller methods, one to register the multer middleware and process the file, and the other to save it to the MongoDB (in this case attached to the club in the request). 因此,如您所见,使用multer非常简单,您真正需要的只是一种上传徽标的途径,它具有两种控制器方法,一种用于注册multer中间件并处理文件,另一种用于将其保存到MongoDB(在这种情况下,请附加到俱乐部)。

Just make sure that ng-file-upload uses the same field name for uploading the file as multer is expecting. 只需确保ng-file-upload使用与multer期望的相同的字段名称来上传文件。 In the above example that's "logo". 在上面的示例中为“徽标”。 If you're unsure, check in the request what your client app is sending to the server and make sure the server app is expecting the same field name. 如果不确定,请在请求中签入客户端应用程序发送到服务器的内容,并确保服务器应用程序使用相同的字段名称。

Let me know if you have further trouble. 让我知道您是否还有其他麻烦。

You can use formidable and gridfs-stream 您可以使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM