Подтвердить что ты не робот

Загрузка изображений с помощью Mongoose, Express и AngularJS

Я знаю, что это было задано много раз раньше, и я прочитал почти все, что мог найти о предмете, а именно:

qaru.site/info/294487/...

Загрузка изображений с помощью Node.js, Express и Mongoose

Это лучшее, что я нашел до сих пор. Моя проблема заключается в том, что они все еще не очень ясны, в этом отношении очень мало документации по этому поводу, и обсуждение кажется нацеленным на людей, которые намного более продвинуты, чем я.

Так что с этим мне действительно понравилось бы, если бы кто-нибудь мог пропустить меня, хотя как загружать изображения с помощью Mongoose, Express и AngularJS. Я фактически использую fullstack MEAN. (этот генератор, если быть точным, https://github.com/DaftMonk/generator-angular-fullstack)

AddController:

'use strict';

angular.module('lumicaApp')
  .controller('ProjectAddCtrl', ['$scope', '$location', '$log', 'projectsModel', 'users', 'types', function ($scope, $location, $log, projectsModel, users, types) {
    $scope.dismiss = function () {
      $scope.$dismiss();
    };

        $scope.users = users;
        $scope.types = types;

    $scope.project = {
            name: null,
            type: null,
            images: {
                thumbnail: null // I want to add the uploaded images _id here to reference with mongoose populate.
            },
            users: null
        };

        $scope.save = function () {
            $log.info($scope.project);
            projectsModel.post($scope.project).then(function (project) {
        $scope.$dismiss();
            });
        }

  }]);

Я хочу добавить ссылку на идентификатор изображений на project.images.thumbnail, но я хочу сохранить всю информацию внутри объекта изображения с помощью следующей схемы:

'use strict';

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

    var ImageSchema = new Schema({
      fileName: String,
      url: String,
      contentType: String,
      size: String,
      dimensions: String
    });

    module.exports = mongoose.model('Image', ImageSchema);

Я также добавил следующие https://github.com/nervgh/angular-file-upload в мои пакеты bower.

Как я уже сказал, я просто не могу понять, как связать все это вместе. И я даже не уверен, что то, что я пытаюсь сделать, это правильный путь.

----------------------------------------------- ---------------------------\

UPDATE:

Вот что я теперь имею, я добавил несколько комментариев, подробно описывающих, как я хотел бы работать, к сожалению, мне все еще не удалось заставить это работать, я даже не могу получить изображение, чтобы начать загрузку, неважно загрузка на S3. Извините, что это боль, но я просто нахожу это особенно запутанным, что меня удивляет.

клиент/приложение/людей/добавить/add.controller.js

'use strict';

angular.module('lumicaApp')
    .controller('AddPersonCtrl', ['$scope', '$http', '$location', '$window', '$log', 'Auth', 'FileUploader', 'projects', 'usersModel', function ($scope, $http, $location, $window, $log, Auth, FileUploader, projects, usersModel) {
        $scope.dismiss = function () {
            $scope.$dismiss();
        };

        $scope.newResource = {};

        // Upload Profile Image
        $scope.onUploadSelect = function($files) {
            $scope.newResource.newUploadName = $files[0].name;

            $http
                .post('/api/uploads', {
                    uploadName: newResource.newUploadName,
                    upload: newResource.newUpload
                })
                .success(function(data) {
                    newResource.upload = data; // To be saved later
                });
        };

        $log.info($scope.newResource);

        //Get Projects List
        $scope.projects = projects;

        //Register New User
        $scope.user = {};
        $scope.errors = {};


        $scope.register = function(form) {
            $scope.submitted = true;

            if(form.$valid) {
                Auth.createUser({
                    firstName: $scope.user.firstName,
                    lastName: $scope.user.lastName,
                    username: $scope.user.username,
                    profileImage: $scope.user.profileImage, // I want to add the _id reference for the image here to I can populate it with 'ImageSchema' using mongoose to get the image details(Name, URL, FileSize, ContentType, ETC)
                    assigned: {
                        teams: null,
                        projects: $scope.user.assigned.projects
                    },
                    email: $scope.user.email,
                    password: $scope.user.password
                })
                    .then( function() {
                        // Account created, redirect to home
                        //$location.path('/');
                        $scope.$dismiss();
                    })
                    .catch( function(err) {
                        err = err.data;
                        $scope.errors = {};

                        // Update validity of form fields that match the mongoose errors
                        angular.forEach(err.errors, function(error, field) {
                            form[field].$setValidity('mongoose', false);
                            $scope.errors[field] = error.message;
                        });
                    });
            }
        };

        $scope.loginOauth = function(provider) {
            $window.location.href = '/auth/' + provider;
        };

    }]);

server/api/image/image.model.js Я хотел бы сохранить здесь всю информацию об изображении и использовать ее для заполнения profileImage в контроллере людей.

'use strict';

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

    var ImageSchema = new Schema({
      fileName: String,
      url: String, // Should store the URL of image on S3.
      contentType: String,
      size: String,
      dimensions: String
    });

    module.exports = mongoose.model('Image', ImageSchema);

клиент/приложение/людей/добавить/add.jade

.modal-header
    h3.modal-title Add {{ title }}
.modal-body
    form(id="add-user" name='form', ng-submit='register(form)', novalidate='')
        .form-group(ng-class='{ "has-success": form.firstName.$valid && submitted,\
        "has-error": form.firstName.$invalid && submitted }')
            label First Name
            input.form-control(type='text', name='firstName', ng-model='user.firstName', required='')
            p.help-block(ng-show='form.firstName.$error.required && submitted')
                | First name is required

        .form-group(ng-class='{ "has-success": form.lastName.$valid && submitted,\
        "has-error": form.lastName.$invalid && submitted }')
            label Last Name
            input.form-control(type='text', name='lastName', ng-model='user.lastName', required='')
            p.help-block(ng-show='form.lastName.$error.required && submitted')
                | Last name is required

        .form-group(ng-class='{ "has-success": form.username.$valid && submitted,\
        "has-error": form.username.$invalid && submitted }')
            label Username
            input.form-control(type='text', name='username', ng-model='user.username', required='')
            p.help-block(ng-show='form.username.$error.required && submitted')
                | Last name is required

        // Upload Profile Picture Here
        .form-group
            label Profile Image
            input(type="file" ng-file-select="onUploadSelect($files)" ng-model="newResource.newUpload")

        .form-group(ng-class='{ "has-success": form.email.$valid && submitted,\
        "has-error": form.email.$invalid && submitted }')
            label Email
            input.form-control(type='email', name='email', ng-model='user.email', required='', mongoose-error='')
            p.help-block(ng-show='form.email.$error.email && submitted')
                | Doesn't look like a valid email.
            p.help-block(ng-show='form.email.$error.required && submitted')
                | What your email address?
            p.help-block(ng-show='form.email.$error.mongoose')
                | {{ errors.email }}

        .form-group(ng-class='{ "has-success": form.password.$valid && submitted,\
        "has-error": form.password.$invalid && submitted }')
            label Password
            input.form-control(type='password', name='password', ng-model='user.password', ng-minlength='3', required='', mongoose-error='')
            p.help-block(ng-show='(form.password.$error.minlength || form.password.$error.required) && submitted')
                | Password must be at least 3 characters.
            p.help-block(ng-show='form.password.$error.mongoose')
                | {{ errors.password }}

        .form-group
            label Assign Project(s)
            br
            select(multiple ng-options="project._id as project.name for project in projects" ng-model="user.assigned.projects")
        button.btn.btn-primary(ng-submit='register(form)') Save

    pre(ng-bind="user | json")
.modal-footer
    button.btn.btn-primary(type="submit" form="add-user") Save
    button.btn.btn-warning(ng-click='dismiss()') Cancel

сервер/API/загрузки/index.js

'use strict';

var express = require('express');
var controller = require('./upload.controller');

var router = express.Router();

//router.get('/', controller.index);
//router.get('/:id', controller.show);
router.post('/', controller.create);
//router.put('/:id', controller.update);
//router.patch('/:id', controller.update);
//router.delete('/:id', controller.destroy);

module.exports = router;

сервер/API/загрузки/upload.controller.js

'use strict';

var _ = require('lodash');
//var Upload = require('./upload.model');
var aws = require('aws-sdk');
var config = require('../../config/environment');
var randomString = require('../../components/randomString');

// Creates a new upload in the DB.
exports.create = function(req, res) {
    var s3 = new aws.S3();
    var folder = randomString.generate(20); // I guess I do this because when the user downloads the file it will have the original file name.
    var matches = req.body.upload.match(/data:([A-Za-z-+\/].+);base64,(.+)/);

    if (matches === null || matches.length !== 3) {
        return handleError(res, 'Invalid input string');
    }

    var uploadBody = new Buffer(matches[2], 'base64');

    var params = {
        Bucket: config.aws.bucketName,
        Key: folder + '/' + req.body.uploadName,
        Body: uploadBody,
        ACL:'public-read'
    };

    s3.putObject(params, function(err, data) {
        if (err)
            console.log(err)
        else {
            console.log("Successfully uploaded data to my-uploads/" + folder + '/' + req.body.uploadName);
            return res.json({
                name: req.body.uploadName,
                bucket: config.aws.bucketName,
                key: folder
            });
        }
    });
};

function handleError(res, err) {
    return res.send(500, err);
}

сервер/конфигурации/среда/development.js

aws: {
        key: 'XXXXXXXXXXXX',
        secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        region: 'sydney',
        bucketName: 'my-uploads'
    }
4b9b3361

Ответ 1

Весь этот код прямо из проекта, который сильно зависит от этого для больших загрузок файлов и изображений. Определенно проверка https://github.com/nervgh/angular-file-upload

На мой взгляд, где-то:

<div class="form-group">
  <label>File Upload</label>
  <input type="file" ng-file-select="onUploadSelect($files)" ng-model="newResource.newUpload">
</div>

Используя модуль angularFileUpload, я тогда в контроллере:

$scope.onUploadSelect = function($files) {
  $scope.newResource.newUploadName = $files[0].name;
};

https://github.com/nervgh/angular-file-upload

Когда пользователь нажимает кнопку "Загрузить", это выполняется, когда я отправляю загружаемый файл:

$http
  .post('/api/uploads', {
    uploadName: newResource.newUploadName,
    upload: newResource.newUpload
  })
  .success(function(data) {
    newResource.upload = data; // To be saved later
  });

Этот запрос отправляется контроллеру, который выглядит примерно так:

'use strict';

var _ = require('lodash');
var aws = require('aws-sdk');
var config = require('../../config/environment');
var randomString = require('../../components/randomString');

// Creates a new upload in the DB.
exports.create = function(req, res) {
  var s3 = new aws.S3();
  var folder = randomString.generate(20); // I guess I do this because when the user downloads the file it will have the original file name.
  var matches = req.body.upload.match(/data:([A-Za-z-+\/].+);base64,(.+)/);

  if (matches === null || matches.length !== 3) {
    return handleError(res, 'Invalid input string');
  }

  var uploadBody = new Buffer(matches[2], 'base64');

  var params = {
    Bucket: config.aws.bucketName,
    Key: folder + '/' + req.body.uploadName,
    Body: uploadBody,
    ACL:'public-read'
  };

  s3.putObject(params, function(err, data) {
    if (err)
      console.log(err)
    else {
      console.log("Successfully uploaded data to csk3-uploads/" + folder + '/' + req.body.uploadName);
      return res.json({
        name: req.body.uploadName,
        bucket: config.aws.bucketName,
        key: folder
      });
    }
   });
};

function handleError(res, err) {
  return res.send(500, err);
}

сервер/компоненты/randomString/index.js

'use strict';

module.exports.generate = function(textLength) {
  textLength = textLength || 10;
  var text = '';
  var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

  for(var i = 0; i < textLength; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return text;
};

enter image description here

server/config/environment/development.js

enter image description here

server/api/upload/upload.controller.js

enter image description here

Ответ 2

Именно так я использовал MEAN.JS для загрузки файлов.

Model

var UserSchema = new mongoose.Schema({
name:{type:String,required:true},
photo:Buffer  // Image
});

Серверный контроллер

var userPicture = function(req,res){             // Stores Picture for a user matching the ID.
user.findById(req.param('id'), function (err, user) {
    console.log(req.files) // File from Client
    if(req.files.file){   // If the Image exists
        var fs = require('node-fs');
        fs.readFile(req.files.file.path, function (dataErr, data) {
            if(data) {
                user.photo ='';
                user.photo = data;  // Assigns the image to the path.
                user.save(function (saveerr, saveuser) {
                    if (saveerr) {
                        throw saveerr;
                    }
                    res.json(HttpStatus.OK, saveuser);                        
                });
            }
        });
        return
    }
    res.json(HttpStatus.BAD_REQUEST,{error:"Error in file upload"});
});
};

Клиентский контроллер

$scope.saveuserImage =  function(){
    $scope.upload = $upload.upload({  // Using $upload
        url: '/user/'+$stateParams.id+'/userImage',  // Direct Server Call.
        method:'put',
        data:'',  // Where the image is going to be set.
        file: $scope.file
    }).progress(function (evt) {})
        .success(function () {
            var logo = new FileReader();  // FileReader.

            $scope.onAttachmentSelect = function(file){
                logo.onload = function (e) {
                    $scope.image = e.target.result;  // Assigns the image on the $scope variable.
                    $scope.logoName = file[0].name; // Assigns the file name.
                    $scope.$apply();
                };
                logo.readAsDataURL(file[0]);
                $scope.file = file[0];
                $scope.getFileData = file[0].name
            };
            location.reload();
            $scope.file = "";
            $scope.hideUpload = 'true'
        });
    $scope.getFileData = '';
 //        location.reload()
};

Html

ng-file-select используется для получения файла от клиента.

Это отлично работает для меня. Надеюсь, это поможет.

Примечание. Я использовал тег HTML вместо нефрита. Подходящие изменения применимы при использовании нефрита.

Ответ 3

Насколько я могу судить, вы связываете метод FileReader.onload() внутри функции saveUserImage, тогда метод onload никогда не будет вызываться, поскольку функция никогда не связывается, вместо этого пользователь вызывает метод saveUserImage перед редактированием изображения. После этого изображение не будет выбрано, поскольку метод onload() не будет выполнен.

Попробуйте кодировать Client Controller таким образом

//This goes outside your method and will handle the file selection.This must be executed when your `input(type=file)` is created. Then we will use ng-init to bind it.

  $scope.onAttachmentSelect = function(){
        var logo = new FileReader();  // FileReader.
        logo.onload = function (event) {
        console.log("THE IMAGE HAS LOADED");
        var file = event.currentTarget.files[0]
        console.log("FILENAME:"+file.name);
        $scope.image = file; 
        $scope.logoName = file.name; // Assigns the file name.
           $scope.$apply();
           //Call save from here
           $scope.saveuserImage();
        };
        logo.readAsDataURL(file[0]);
        $scope.file = file[0];
       $scope.getFileData = file[0].name
            reader.readAsDataURL(file);
    };


//The save method is called from the onload function (when you add a new file)
$scope.saveuserImage =  function(){
    console.log("STARGING UPLOAD");
    $scope.upload = $upload.upload({  // Using $upload
        url: '/user/'+$stateParams.id+'/userImage',  
        method:'put'
        data:,   $scope.image
        file: $scope.file
    }).progress(function (evt) {})
        .success(function () {
            location.reload();
            $scope.file = "";
            $scope.hideUpload = 'true'
        });
    $scope.getFileData = '';
 //        location.reload()
};

HTML.

//There is the ng-init call to binding function onAttachmentSelect
<div class="form-group">
  <label>File Upload</label>
  <input type="file" ng-init="onAttachmentSelect" ng-model="newResource.newUpload">
</div>

Надеюсь, что эта подсказка поможет вам

ИЗМЕНИТЬ *

Попробуем объяснить вам различные шаги, которые вы должны выполнить, чтобы проверить свой код:

1.- Показывается ваш вход [type = file]? Если отображается, выберите изображение

2.- Является ли ваш вход вызовом onload при изменении выбранного изображения? (console.log следует печатать с моей версией кода)

3.- Если он был вызван. Сделайте необходимые операции перед отправкой, внутри метода onload (если возможно)

4.- Когда этот метод завершил выполнение желаемых изменений. Сообщите с помощью ng-модели или, как вам угодно, переменной в объекте, который вы подготовили для загрузки, с строкой base64, сгенерированной в методе onload.

При достижении этой точки не забудьте проверить, что:

Поскольку очень большие изображения могут быть отправлены через json с base64, очень важно запомнить изменение минимального размера json в Express.js для вашего приложения, чтобы предотвратить отклонения. Это делается, например, на вашем сервере /app.js следующим образом:

var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb'}));

Помните также, что метод reader.readAsDataURL(file) даст вам строку base64, которая может действовать как src изображения. Тебе не нужно больше этого. Этот base64 - это то, что вы можете сохранить в мангусте. Затем вы можете использовать ng-model для отправки переменной, содержащей base64 в форме, с помощью кнопки "отправить".

Затем в конечной точке Express.js, которая будет обрабатывать вашу форму, вы сможете декодировать строку base64 в файл или сохранить base64 непосредственно на mongoose (сохранение изображений в db не рекомендуется, если много изображений должно быть загружено или большие, потому что запрос mongoDB будет очень медленным).

Надеюсь, вы сможете решить эти показания. Если у вас все еще есть некоторые сомнения, прокомментируйте и я попытаюсь помочь.

Ответ 4

Я также использую noob с помощью MEANJS, и именно так я сделал это, используя ng-flow + FileReader:

Ввод HTML:

<div flow-init 
        flow-files-added="processFiles($files)"
        flow-files-submitted="$flow.upload()" 
        test-chunks="false">
        <!-- flow-file-error="someHandlerMethod( $file, $message, $flow )"     ! need to implement-->
        <div class="drop" flow-drop ng-class="dropClass">
            <span class="btn btn-default" flow-btn>Upload File</span>
            <span class="btn btn-default" flow-btn flow-directory ng-show="$flow.supportDirectory">Upload Folder</span>
            <b>OR</b>
            Drag And Drop your file here
        </div>

контроллер:

    $scope.uploadedImage = 0;

    // PREPARE FILE FOR UPLOAD
    $scope.processFiles = function(flow){
        var reader = new FileReader();
        reader.onload = function(event) {
            $scope.uploadedImage = event.target.result;
        };
        reader.onerror = function(event) {
            console.error('File could not be read! Code ' + event.target.error.code);
        };
        reader.readAsDataURL(flow[0].file);
    };

И на стороне сервера переменная на модели, получающая значение uploadedImage, имеет тип строки.

Получение его с сервера не требовало никакого преобразования:

<img src={{result.picture}} class="pic-image" alt="Pic"/>

Теперь просто нужно выяснить, что делать с большими файлами...