У меня проблемы с загрузкой изображений с использованием nodejs, express, pug и multer. Я добавил пример локальной библиотеки Mozilla в пример локальной галереи и добавил возможность загрузки изображений. По сути, я получаю сообщение об ошибке, когда включаю enctype = 'multipart / form-data' в свое представление мопса, которое обрабатывает почтовый запрос для формы загрузки изображения. Ниже приведена часть моего кода, вытекающего из определения маршрутов ...
catalog. js
// Require packages.
// const multer = require('multer');
var express = require('express');
var router = express.Router();
// Require our controllers.
var gallery_controller = require('../controllers/galleryController');
var author_controller = require('../controllers/authorController');
var tag_controller = require('../controllers/tagController');
var media_controller = require('../controllers/mediaController');
// Require our middleware.
var upload_middleware = require('../middleware/uploadMiddleware');
/// GALLERY ROUTES ///
// GET catalog home page.
router.get('/', gallery_controller.index);
// GET request for creating a Gallery. NOTE This must come before routes that display Gallery (uses id).
router.get('/gallery/create', gallery_controller.gallery_create_get);
// POST request for creating Gallery.
router.post('/gallery/create', gallery_controller.gallery_create_post);
// GET request to delete Gallery.
router.get('/gallery/:id/delete', gallery_controller.gallery_delete_get);
// POST request to delete Gallery.
router.post('/gallery/:id/delete', gallery_controller.gallery_delete_post);
// GET request to update Gallery.
router.get('/gallery/:id/update', gallery_controller.gallery_update_get);
// POST request to update Gallery.
router.post('/gallery/:id/update', gallery_controller.gallery_update_post);
// GET request for one Gallery.
router.get('/gallery/:id', gallery_controller.gallery_detail);
// GET request for list of all Gallery.
router.get('/galleries', gallery_controller.gallery_list);
/// AUTHOR ROUTES ///
// GET request for creating Author. NOTE This must come before route for id (i.e. display author).
router.get('/author/create', author_controller.author_create_get);
// POST request for creating Author.
router.post('/author/create', author_controller.author_create_post);
// GET request to delete Author.
router.get('/author/:id/delete', author_controller.author_delete_get);
// POST request to delete Author
router.post('/author/:id/delete', author_controller.author_delete_post);
// GET request to update Author.
router.get('/author/:id/update', author_controller.author_update_get);
// POST request to update Author.
router.post('/author/:id/update', author_controller.author_update_post);
// GET request for one Author.
router.get('/author/:id', author_controller.author_detail);
// GET request for list of all Authors.
router.get('/authors', author_controller.author_list);
/// TAG ROUTES ///
// GET request for creating a Tag. NOTE This must come before route that displays Tag (uses id).
router.get('/tag/create', tag_controller.tag_create_get);
// POST request for creating Tag.
router.post('/tag/create', tag_controller.tag_create_post);
// GET request to delete Tag.
router.get('/tag/:id/delete', tag_controller.tag_delete_get);
// POST request to delete Tag.
router.post('/tag/:id/delete', tag_controller.tag_delete_post);
// GET request to update Tag.
router.get('/tag/:id/update', tag_controller.tag_update_get);
// POST request to update Tag.
router.post('/tag/:id/update', tag_controller.tag_update_post);
// GET request for one Tag.
router.get('/tag/:id', tag_controller.tag_detail);
// GET request for list of all Tag.
router.get('/tags', tag_controller.tag_list);
/// MEDIA ROUTES ///
// GET request for creating a Media. NOTE This must come before route that displays Media (uses id).
router.get('/media/create', media_controller.media_create_get);
// POST request for creating Media.
// router.post('/media/create', upload_middleware.uploadFile, media_controller.media_create_post);
// router.post('/media/create', upload_middleware.uploadFile, media_controller.media_create_get);
router.post('/media/create', media_controller.media_create_post);
// GET request to delete Media.
router.get('/media/:id/delete', media_controller.media_delete_get);
// POST request to delete Media.
router.post('/media/:id/delete', media_controller.media_delete_post);
// GET request to update Media.
router.get('/media/:id/update', media_controller.media_update_get);
// POST request to update Media.
router.post('/media/:id/update', media_controller.media_update_post);
// GET request for one Media.
router.get('/media/:id', media_controller.media_detail);
// GET request for list of all Media.
router.get('/media', media_controller.media_list);
module.exports = router;
media_Controller. js
// Require models.
var Media = require('../models/media')
var Gallery = require('../models/gallery')
var async = require('async')
// Require packages.
const { body,validationResult } = require('express-validator/check');
const { sanitizeBody } = require('express-validator/filter');
// Display list of all Medias.
exports.media_list = function(req, res, next) {
Media.find()
.populate('gallery')
.exec(function (err, list_media) {
if (err) { return next(err); }
// Successful, so render.
res.render('media_list', { title: 'Media List', media_list: list_media});
})
};
// Display detail page for a specific Media.
exports.media_detail = function(req, res, next) {
Media.findById(req.params.id)
.populate('gallery')
.exec(function (err, media) {
if (err) { return next(err); }
if (media==null) { // No results.
var err = new Error('Gallery copy not found');
err.status = 404;
return next(err);
}
// Successful, so render.
res.render('media_detail', { title: 'Gallery:', media: media});
})
};
// Display Media create form on GET.
exports.media_create_get = function(req, res, next) {
Gallery.find({},'title')
.exec(function (err, galleries) {
if (err) { return next(err); }
// Successful, so render.
res.render('media_form', {title: 'Create Media', gallery_list:galleries } );
});
};
// Handle Media create on POST.
exports.media_create_post = [
// Validate fields.
body('gallery', 'Gallery must be specified').isLength({ min: 1 }).trim(),
body('name', 'Name must be specified').isLength({ min: 1 }).trim(),
body('type', 'Type must be specified').isLength({ min: 1 }).trim(),
body('UMID', 'UMID must be specified').isLength({ min: 1 }).trim(),
body('created', 'Invalid date').optional({ checkFalsy: true }).isISO8601(),
// Sanitize fields.
sanitizeBody('gallery').escape(),
sanitizeBody('name').escape(),
sanitizeBody('type').escape(),
sanitizeBody('UMID').escape(),
sanitizeBody('created').toDate(),
// Process request after validation and sanitization.
(req, res, next) => {
// Extract the validation errors from a request.
const errors = validationResult(req);
// Create a Media object with escaped and trimmed data.
var media = new Media(
{ gallery: req.body.gallery,
name: req.body.name,
type: req.body.type,
UMID: req.body.UMID,
created: req.body.created
});
if (!errors.isEmpty()) {
// There are errors. Render form again with sanitized values and error messages.
Gallery.find({},'title')
.exec(function (err, galleries) {
if (err) { return next(err); }
// Successful, so render.
res.render('media_form', { title: 'Create Media', gallery_list : galleries, selected_gallery : media.gallery._id , errors: errors.array(), media:media });
});
return;
}
else {
// Data from form is valid
media.save(function (err) {
// Check is errors.
console.log('Checking for errors.')
if (err) { return next(err); }
// Successful - redirect to new record.
console.log('Successful - redirect to new record. ' + media.url)
res.redirect(media.url);
});
}
}
];
// Display Media delete form on GET.
exports.media_delete_get = function(req, res, next) {
Media.findById(req.params.id)
.populate('gallery')
.exec(function (err, media) {
if (err) { return next(err); }
if (media==null) { // No results.
res.redirect('/catalog/media');
}
// Successful, so render.
res.render('media_delete', { title: 'Delete Media', media: media});
})
};
// Handle Media delete on POST.
exports.media_delete_post = function(req, res, next) {
// Assume valid Media id in field.
Media.findByIdAndRemove(req.body.id, function deleteMedia(err) {
if (err) { return next(err); }
// Success, so redirect to list of Media items.
res.redirect('/catalog/media');
});
};
// Display Media update form on GET.
exports.media_update_get = function(req, res, next) {
// Get gallery, authors and genres for form.
async.parallel({
media: function(callback) {
Media.findById(req.params.id).populate('gallery').exec(callback)
},
galleries: function(callback) {
Gallery.find(callback)
},
}, function(err, results) {
if (err) { return next(err); }
if (results.media==null) { // No results.
var err = new Error('Gallery copy not found');
err.status = 404;
return next(err);
}
// Success.
res.render('media_form', { title: 'Update Media', gallery_list : results.galleries, selected_gallery : results.media.gallery._id, media:results.media });
});
};
// Handle Media update on POST.
exports.media_update_post = [
// Validate fields.
body('gallery', 'Gallery must be specified').isLength({ min: 1 }).trim(),
body('name', 'Name must be specified').isLength({ min: 1 }).trim(),
body('type', 'Type must be specified').isLength({ min: 1 }).trim(),
body('UMID', 'UMID must be specified').isLength({ min: 1 }).trim(),
body('created', 'Invalid date').optional({ checkFalsy: true }).isISO8601(),
// Sanitize fields.
sanitizeBody('gallery').escape(),
sanitizeBody('name').escape(),
sanitizeBody('type').escape(),
sanitizeBody('UMID').escape(),
sanitizeBody('created').toDate(),
// Process request after validation and sanitization.
(req, res, next) => {
// Extract the validation errors from a request.
const errors = validationResult(req);
// Create a Media object with escaped/trimmed data and current id.
var media = new Media(
{ gallery: req.body.gallery,
name: req.body.name,
type: req.body.type,
UMID: req.body.UMID,
created: req.body.created,
_id: req.params.id
});
if (!errors.isEmpty()) {
// There are errors so render the form again, passing sanitized values and errors.
Gallery.find({},'title')
.exec(function (err, galleries) {
if (err) { return next(err); }
// Successful, so render.
res.render('media_form', { title: 'Update Media', gallery_list : galleries, selected_gallery : media.gallery._id , errors: errors.array(), media:media });
});
return;
}
else {
// Data from form is valid.
Media.findByIdAndUpdate(req.params.id, media, {}, function (err,themedia) {
if (err) { return next(err); }
// Successful - redirect to detail page.
res.redirect(themedia.url);
});
}
}
];
media_form. мопс
extends layout
block content
h1=title
form(method='POST' action='' enctype='multipart/form-data')
div.form-group
label(for='gallery') Gallery:
select#gallery.form-control(type='select', placeholder='Select gallery' name='gallery' required='true' )
- gallery_list.sort(function(a, b) {let textA = a.title.toUpperCase(); let textB = b.title.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;});
for gallery in gallery_list
option(value=gallery._id, selected=(selected_gallery==gallery._id.toString() ? 'selected' : false) ) #{gallery.title}
div.form-group
label(for='name') Name:
input#name.form-control(type='text', placeholder='Please enter a name...' name='name' required='true' value=(undefined===media ? '' : media.name) )
div.form-group
label(for='image') Media:
input#image.form-control(type='file', placeholder='Please select a media file to upload...' name='image' onchange="readSingleFile(this.files)" )
div.form-group
label(for='type') Type:
select#type.form-control(type='select', placeholder='Select a type' name='type' required='true' )
option(value='BMP' selected=(undefined===media || media.type!='BMP' ? false:'selected')) BMP
option(value='GIF' selected=(undefined===media || media.type!='GIF' ? false:'selected')) GIF
option(value='ICO' selected=(undefined===media || media.type!='ICO' ? false:'selected')) ICO
option(value='JPG' selected=(undefined===media || media.type!='JPG' ? false:'selected')) JPG
option(value='PNG' selected=(undefined===media || media.type!='PNG' ? false:'selected')) PNG
option(value='SVG' selected=(undefined===media || media.type!='SVG' ? false:'selected')) SVG
option(value='WebP' selected=(undefined===media || media.type!='WebP' ? false:'selected')) WebP
div.form-group
label(for='UMID') UMID:
input#UMID.form-control(type='text', placeholder='Publisher and date information' name='UMID' required='true' value=(undefined===media ? '' : media.UMID) )
div.form-group
label(for='created') Date when media was created:
input#created.form-control(type='date', name='created' value=(undefined===media ? '' : media.created_yyyy_mm_dd))
button.btn.btn-primary(type='submit') Submit
if errors
ul
for error in errors
li!= error.msg
медиа. js
var mongoose = require('mongoose');
var moment = require('moment');
var Schema = mongoose.Schema;
var MediaSchema = new Schema({
gallery: {type: Schema.ObjectId, ref: 'Gallery', required: true}, // Reference to the associated gallery.
name: {type: String, required: true},
image: {data: Buffer, contentType: String},
type: {type: String, required: true, enum:['BMP', 'GIF', 'ICO', 'JPG', 'PNG', 'SVG', 'WebP'], default:'JPG'},
UMID: {type: String, required: true},
created: {type: Date, default: Date.now},
});
// Virtual for this media object's URL.
MediaSchema
.virtual('url')
.get(function () {
return '/catalog/media/'+this._id;
});
MediaSchema
.virtual('created_formatted')
.get(function () {
return moment(this.created).format('MMMM Do, YYYY');
});
MediaSchema
.virtual('created_yyyy_mm_dd')
.get(function () {
return moment(this.created).format('YYYY-MM-DD');
});
// Export model.
module.exports = mongoose.model('Media', MediaSchema);
приложение. js
// Modules
const compression = require('compression');
const cookieParser = require('cookie-parser');
const createError = require('http-errors');
const express = require('express');
const helmet = require('helmet');
const logger = require('morgan');
const mongoose = require('mongoose');
const path = require('path');
const PATH = './public/uploads';
var app = express();
var catalogRouter = require('./routes/catalog');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
// Set up mongoose connection
var dev_db_url = 'mongodb://admin:N!md%40@localhost:27017/Gallery';
var mongoDB = process.env.MONGODB_URI || dev_db_url;
mongoose.connect(mongoDB, { useNewUrlParser: true });
mongoose.Promise = global.Promise;
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// logger setup
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(helmet());
app.use(compression()); // Compress all routes
// static files
app.use(express.static(path.join(__dirname, 'public')));
// routes.
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/catalog', catalogRouter); // Add catalog routes to middleware chain.
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Пожалуйста, помогите ???