Почему req.file отображается "undefined" при попытке загрузить файл с помощью multer? - PullRequest
0 голосов
/ 12 июля 2020

Я пытаюсь разрешить пользователю загружать файл с помощью формы, тогда изображение должно обрабатываться с помощью multer в моем файле контроллера. По какой-то причине, когда я использую upload.single ('foobar'), он возвращается как «undefined» и портит остальную часть моего приложения. В частности, когда я запускаю код, мой обработчик ошибок в файле createTour. js возвращает предупреждение, которое гласит: «Невозможно прочитать свойство 'imageCover' из undefined». Любая помощь будет оценена. Если полезно, вот GitHub .

Вот код файла контроллера (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();
});

Это форма на передней панели, написано на 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

Это код на стороне клиента JS, который обрабатывает данные формы, отправляя их в 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);
  }
};

Наконец, не уверен, что это полезно, но это код файла маршрутизатора (tourRoutes. js):

router
  .route('/')
  .get(tourController.getAllTours)
  .post(
    authController.protect,
    authController.restrictTo('user', 'admin', 'lead-guide'),
    tourController.uploadTourImages,
    tourController.resizeTourImages,
    tourController.createTour
  );

1 Ответ

1 голос
/ 12 июля 2020

Поскольку вы используете multer.single(), загруженный файл будет помещен в req.file как объект (см. https://github.com/expressjs/multer#singlefieldname). Однако вы обращаетесь к нему как к массиву в этой строке:

await sharp(req.file.imageCover[0].buffer)

Изменение этого значения на

await sharp(req.file.imageCover.buffer)

должно решить проблему.

Также на клиенте - сторона кажется, что вы на самом деле не загружаете form-data. Вам нужно изменить его на что-то вроде:

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'
    }
})
...