API для узла загрузки фотографий - PullRequest
0 голосов
/ 04 мая 2020

Я новичок в узле. Я создаю маршрут для загрузки фотографии в файловую систему. Я получил реализацию от загрузки фотографий Digital Ocean

Я использую архитектуру MVC. Вот код для контроллеров

   exports.uploadImage = asyncHandler (async (req, res, next) =>{
        const event = await Event.findById(req.params.id);

        if(!event){
                return next (new ErrorResponse (`No event found with id of ${req.params.id}`, 404));

        }

        // Check if the user is the event owner or an admin

        if(event.user.toString()!==req.user.id && req.user.role !=='admin'){
                return   next(new ErrorResponse(`User with id ${req.user.id} is not allowed to update this event`, 401));       
        }

//      Check if files exist
        if (!req.files) {
                return next (new ErrorResponse (`Please upload photo for event`, 400));   
        }

        let files;
        const file = req.file.filename;
        const matches = file.match(/^(.+?)_.+?\.(.+)$/i);
        if (matches) {
                files = _.map(['lg', 'md', 'sm'], function(size) {
                return matches[1] + '_' + size + '.' + matches[2];
                });

        } else {
                files = [file];
        }

        files = _.map(files, function(file) {
                const port = req.app.get('port');
                const base = req.protocol + '://' + req.hostname + (port ? ':' + port : '');
                const url = path.join(req.file.baseUrl, file).replace(/[\\\/]+/g, '/').replace(/^[\/]+/g, '');

                return (req.file.storage == 'local' ? base : '') + '/' + url;
         });

         await Event.findByIdAndUpdate(req.params.id, { photo: files });
         res.status(200).json({
                 success: true,
                 data: files
         });

});

Вот импорт molule

    const ErrorResponse = require('../utils/errorResponse');
const asyncHandler = require('../middleware/asyncHandler');
const Event = require('../models/Event');
// File upload modules
const _ = require('lodash');
const path = require('path');
const multer = require('multer');
const ImageStorage = require('../utils/imageStorage');

Файл маршрута

const express = require ('express');
const {uploadImage} = require('../controllers/events');
const Event = require('../models/Event');
// Protect and authorize routes requiring authentication
const { protect, authorize } = require('../middleware/auth');
const router = express.Router();
router.route('/:id/uploads/photo').put(protect, authorize('admin', 'agent'), uploadImage); 

Загрузка фото находится в папке utils

// Load dependencies
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const Jimp = require('jimp');
const crypto = require('crypto');
const mkdirp = require('mkdirp');
const concat = require('concat-stream');
const streamifier = require('streamifier');

// Configure UPLOAD_PATH
// process.env.IMAGE_FIELD contains uploads/images
const UPLOAD_PATH = path.resolve(__dirname, '..', process.env.IMAGE_FIELD);

// create a multer storage engine
const ImageStorage = function(options) {

// this serves as a constructor
function ImageStorage(opts) {

        const baseUrl = process.env.IMAGE_BASE_URL;

        const allowedStorageSystems = ['local']; //Use AWS for production
        const allowedOutputFormats = ['jpg', 'png', 'gif'];

        // fallback for the options
        const defaultOptions = {
        storage: process.env.STORAGE_LOCATION, //Add
        output: 'png',
        greyscale: false,
        quality: 70,
        square:false,
        threshold: 500,
        responsive: true,
        };

        // extend default options with passed options
        let options = (opts && _.isObject(opts)) ? _.pick(opts, _.keys(defaultOptions)) : {

        };
        options = _.extend(defaultOptions, options);

        // check the options for correct values and use fallback value where necessary
        this.options = _.forIn(options, function(value, key, object) {

        switch (key) {

        case 'square':
        case 'greyscale':
        case 'responsive':
        object[key] = _.isBoolean(value) ? value : defaultOptions[key];
        break;

        case 'storage':
        value = String(value).toLowerCase();
        object[key] = _.includes(allowedStorageSystems, value) ? value : defaultOptions[key];
        break;

        case 'output':
        value = String(value).toLowerCase();
        object[key] = _.includes(allowedOutputFormats, value) ? value : defaultOptions[key];
        break;

        case 'quality':
        value = _.isFinite(value) ? value : Number(value);
        object[key] = (value && value >= 0 && value <= 100) ? value : defaultOptions[key];
        break;

        case 'threshold':
        value = _.isFinite(value) ? value : Number(value);
        object[key] = (value && value >= 0) ? value : defaultOptions[key];
        break;

        }

        });

        // set the upload path
        this.uploadPath = this.options.responsive ? path.join(UPLOAD_PATH, 'responsive') : UPLOAD_PATH;

        // set the upload base url
        this.uploadBaseUrl = this.options.responsive ? path.join(baseUrl, 'responsive') : baseUrl;

        if (this.options.storage == 'local') {
        // if upload path does not exist, create the upload path structure
        !fs.existsSync(this.uploadPath) && mkdirp.sync(this.uploadPath);
        }

        }


        // this generates a random cryptographic filename
        ImageStorage.prototype._generateRandomFilename = function() {
        // create pseudo random bytes
        const bytes = crypto.pseudoRandomBytes(32);

        // create the md5 hash of the random bytes
        const checksum = crypto.createHash('MD5').update(bytes).digest('hex');

        // return as filename the hash with the output extension
        return checksum + '.' + this.options.output;
        };

        // this creates a Writable stream for a filepath
        ImageStorage.prototype._createOutputStream = function(filepath, cb) {

        // create a reference for this to use in local functions
        const that = this;

        // create a writable stream from the filepath
        const output = fs.createWriteStream(filepath);

        // set callback fn as handler for the error event
        output.on('error', cb);

        // set handler for the finish event
        output.on('finish', function() {
        cb(null, {
        destination: that.uploadPath,
        baseUrl: that.uploadBaseUrl,
        filename: path.basename(filepath),
        storage: that.options.storage
        });
        });

        // return the output stream
        return output;
        };


        // this processes the Jimp image buffer
ImageStorage.prototype._processImage = function(image, cb) {

        // create a reference for this to use in local functions
        const that = this;

        let batch = [];

        // the responsive sizes
        const sizes = ['lg', 'md', 'sm'];

        const filename = this._generateRandomFilename();

        let mime = Jimp.MIME_PNG;
        // The default value of the mime type to be an .pgn

        // create a clone of the Jimp image
        let clone = image.clone();

        // fetch the Jimp image dimensions
        const width = clone.bitmap.width;
        const height = clone.bitmap.height;
        const square = Math.min(width, height);
        const threshold = this.options.threshold;

        // resolve the Jimp output mime type
        switch (this.options.output) {
        case 'jpg':
        mime = Jimp.MIME_JPEG;
        break;
        case 'png':
        default:
        mime = Jimp.MIME_PNG;
        break;
        }

        // auto scale the image dimensions to fit the threshold requirement
        if (threshold && square > threshold) {
        clone = (square == width) ? clone.resize(threshold, Jimp.AUTO) : clone.resize(Jimp.AUTO, threshold);
        }

        // // crop the image to a square if enabled
        // if (this.options.square) {

        // if (threshold) {
        // square = Math.min(square, threshold);
        // }

        // // // fetch the new image dimensions and crop
        // // clone = clone.crop((clone.bitmap.width: square) / 2, (clone.bitmap.height: square) / 2, square, square);
        //  }

        // convert the image to greyscale if enabled
        if (this.options.greyscale) {
        clone = clone.greyscale();
        }

        // set the image output quality
        clone = clone.quality(this.options.quality);

        if (this.options.responsive) {

        // map through the responsive sizes and push them to the batch
        batch = _.map(sizes, function(size) {

        let outputStream;

        let image = null;
        let filepath = filename.split('.');

        // create the complete filepath and create a writable stream for it
        filepath = filepath[0] + '_' + size + '.' + filepath[1];
        filepath = path.join(that.uploadPath, filepath);
        outputStream = that._createOutputStream(filepath, cb);

        // scale the image based on the size
        switch (size) {
        case 'sm':
        image = clone.clone().scale(0.3);
        break;
        case 'md':
        image = clone.clone().scale(0.7);
        break;
        case 'lg':
        image = clone.clone();
        break;
        }

        // return an object of the stream and the Jimp image
        return {
        stream: outputStream,
        image
        };
        });

        } else {

        // push an object of the writable stream and Jimp image to the batch
        batch.push({
        stream: that._createOutputStream(path.join(that.uploadPath, filename), cb),
        image: clone
        });

        }

        // process the batch sequence
        _.each(batch, function(current) {
        // get the buffer of the Jimp image using the output mime type
        current.image.getBuffer(mime, function(err, buffer) {
        if (that.options.storage == 'local') {
        // create a read stream from the buffer and pipe it to the output stream
        streamifier.createReadStream(buffer).pipe(current.stream);
        }
        });
        });

        };

        // multer requires this for handling the uploaded file
        ImageStorage.prototype._handleFile = function(req, file, cb) {

        // create a reference for this to use in local functions
        let that = this;

        // create a writable stream using concat-stream that will
        // concatenate all the buffers written to it and pass the
        // complete buffer to a callback fn
        let fileManipulate = concat(function(imageData) {

        // read the image buffer with Jimp
        // it returns a promise
        Jimp.read(imageData)
        .then(function(image) {
        // process the Jimp image buffer
        that._processImage(image, cb);
        })
        .catch(cb);
        });

        // write the uploaded file buffer to the fileManipulate stream
        file.stream.pipe(fileManipulate);

        };

        // multer requires this for destroying file
        ImageStorage.prototype._removeFile = function(req, file, cb) {

        let matches, pathsplit;
        const filename = file.filename;
        const _path = path.join(this.uploadPath, filename);
        let paths = [];

        // delete the file properties
        delete file.filename;
        delete file.destination;
        delete file.baseUrl;
        delete file.storage;

        // create paths for responsive images
        if (this.options.responsive) {
        pathsplit = _path.split('/');
        matches = pathsplit.pop().match(/^(.+?)_.+?\.(.+)$/i);

        if (matches) {
        paths = _.map(['lg', 'md', 'sm'], function(size) {
        return pathsplit.join('/') + '/' + (matches[1] + '_' + size + '.' + matches[2]);
        });
        }
        } else {
        paths = [_path];
        }

        // delete the files from the filesystem
        _.each(paths, function(_path) {
        fs.unlink(_path, cb);
        });

        };


// create a new instance with the passed options and return it
return new ImageStorage(options);

};

// export the storage engine
module.exports = ImageStorage;

Вот папка промежуточного программного обеспечения const ImageStorage = require ('../utils/imageStorage');

const _ = require('lodash');
const path = require('path');
const multer = require('multer');

const photoUpload = (req, res, next) =>{
// setup a new instance of the AvatarStorage engine
const storage = ImageStorage({
square: false,
responsive: true,
greyscale: true,
quality: 90
});

const limits = {
files: 1, // allow only 1 file per request
fileSize: process.env.IMAGE_SIZE, // 1 MB (max file size)
};

const fileFilter = function(req, file, cb) {
// supported image file mimetypes
const allowedMimes = ['image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'];

if (_.includes(allowedMimes, file.mimetype)) {
// allow supported image files
cb(null, true);
} else {
// throw error for invalid files
cb(new Error('Invalid file type. Only jpg, png and gif image files are allowed.'));
}
};

// setup multer
const upload = multer({
storage,
limits,
fileFilter
});

next();

return upload();


};

module.exports = photoUpload()

;

Я только что вызвал промежуточное программное обеспечение на сервер. js файл

const photoUpload = require ('./middleware/storage');
app.use(photoUpload());

Когда я пытался сохранить файл, вот ошибка, которую я получил

path.js:28
    throw new TypeError('Path must be a string. Received ' + inspect(path));
    ^

TypeError: Path must be a string. Received undefined
    at assertPath (path.js:28:11)
    at Object.resolve (path.js:1168:7)
    at Object.<anonymous> (/home/nerd/Backend Projects/ewayv-api/utils/imageStorage.js:13:26)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/home/nerd/Backend Projects/ewayv-api/middleware/storage.js:1:84)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)

Пожалуйста, помогите Спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...