问题
I'm trying to allow a user to upload a file using a form, then the image is supposed to be handled using multer in my controller file. For some reason, when I use upload.single('foobar'), it's coming back as "undefined," and screwing up the rest of my application. Specifically, when I run the code, my error handler in the createTour.js file returns an alert that reads "Cannot read property 'imageCover' of undefined". Any help would be appreciated. If helpful, here's the GitHub.
Here's the controller file code (tourController.js):
const multerStorage = multer.memoryStorage();
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cb(new AppError('Not an image! Please upload images only.', 400), false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter
});
exports.uploadTourImages = upload.single('imageCover');
exports.resizeTourImages = catchAsync(async (req, res, next) => {
console.log(req.file);
req.body.imageCover = `tour-${req.params.id}-${Date.now()}-cover.jpeg`;
await sharp(req.file.imageCover[0].buffer)
.resize(2000, 1333)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/tours/${req.body.imageCover}`);
next();
});
This is the form on the front end, written in Jade (create.pug):
extends base
block content
main.main
.create-form
h2.heading-secondary.ma-bt-lg Create a tour, baby!
form.form.form--create
.form__group
label.form__label(for='name') Tour name
input#name.form__input(type='text', placeholder='you@example.com', required)
.form__group
label.form__label(for='duration') Tour duration
input#duration.form__input(type='number', placeholder='you@example.com', required)
.form__group
label.form__label(for='maxGroupSize') Max group size
input#maxGroupSize.form__input(type='number', placeholder='you@example.com', required)
.form__group
label.form__label(for='difficulty') Difficulty
input#difficulty.form__input(type='text', placeholder='you@example.com', required)
.form__group
label.form__label(for='price') Price
input#price.form__input(type='number', placeholder='you@example.com', required)
.form__group
label.form__label(for='startLocation') Starting Location
input#startLocation.form__input(type='text', placeholder='you@example.com')
.form__group
label.form__label(for='summary') Summary
input#summary.form__input(type='text', placeholder='you@example.com', required)
.form__group
label.form__label(for='description') Detailed Description
input#description.form__input(type='text', placeholder='you@example.com', required)
.form__group
input.form__upload(type='file', accept='image/*', id='imageCover', name='imageCover')
label(for='imageCover') Choose image cover
.form__group
button.btn.btn--green Submit
This is the client side JS code that handles the form data, posting it to the API (createTour.js):
import axios from 'axios';
import { showAlert } from './alerts';
export const createTour = async (
name,
duration,
maxGroupSize,
difficulty,
price,
startLocation,
summary,
description,
imageCover
) => {
try {
const startLocation = {
type: 'Point',
coordinates: [-80.185942, 25.774772],
address: '47 Bowman Lane, Kings Park, NY 11754',
description: 'New Yorkkkkkkkk'
};
const res = await axios({
method: 'POST',
url: 'http://127.0.0.1:8000/api/v1/tours',
data: {
name,
duration,
maxGroupSize,
difficulty,
price,
startLocation,
summary,
description,
imageCover
}
});
if (res.data.status === 'success') {
showAlert('success', 'NEW TOUR CREATED!');
window.setTimeout(() => {
location.assign('/');
}, 1500);
}
} catch (err) {
showAlert('error', err.response.data.message);
}
};
Finally, not sure if it is helpful, but this is the router file code (tourRoutes.js):
router
.route('/')
.get(tourController.getAllTours)
.post(
authController.protect,
authController.restrictTo('user', 'admin', 'lead-guide'),
tourController.uploadTourImages,
tourController.resizeTourImages,
tourController.createTour
);
回答1:
Since you are using multer.single()
the uploaded file will be populated under req.file
as an object (see https://github.com/expressjs/multer#singlefieldname). However, you are accessing it as an array in this line:
await sharp(req.file.imageCover[0].buffer)
Changing this to
await sharp(req.file.imageCover.buffer)
should fix the issue.
Also on the client-side it seems you're not actually doing an form-data
upload. You need to change it to something like:
const formData = new FormData();
formData.set('imageCover', imageCover);
// add some data from the input fields
formData.set('name', name); // add the other remaining
axios.post('http://127.0.0.1:8000/api/v1/tours', formData, {
headers: {
'content-type': 'multipart/form-data'
}
})
来源:https://stackoverflow.com/questions/62864836/why-is-req-file-undefined-for-me-when-trying-to-upload-a-file-using-multer