Как устранить исключение MissingServletRequestPartException: обязательная часть ‘file’ отсутствует при загрузке файлов в API Spring Boot?

Вопрос или проблема

У меня есть метод контроллера в API Spring Boot 3.2.3, который должен принимать загрузку файла.

    @PostMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
    public ResponseEntity<BasicEntityViewModel> postNewEntity(@RequestPart(value = "file") MultipartFile file) {
        System.out.println(file);
        return ResponseEntity.ok(null);
    }

Фактическое содержимое метода выше бесполезно, но это не должно слишком беспокоить, потому что приложение никогда не заходит в него. При отправке запроса, содержащего файл с именем “file”, Spring выдает исключение перед тем, как войти в код метода:

org.springframework.web.multipart.support.MissingServletRequestPartException: Обязательная часть 'file' отсутствует.

Я искал и просматривал множество сообщений в Интернете, но единственные решения, которые предлагались, касались того, что я не использую правильную аннотацию (@RequestPart против @RequestParm против @ModelAttribute), мне следует вручную указать bean MultipartResolver и т.д.

Я проверил с документацией Spring Boot, что метод контроллера построен правильно, и любые MultipartResolvers, которые мне нужны, предоставлялись автонастройкой.

Поиск

Яdebugировал код в течение нескольких часов и нашел важную часть головоломки. Давно я создал пользовательский HttpServletRequest, чтобы позволить моим фильтрам запросов неоднократно считывать тело HttpServletRequest. Он выглядел так:

public class ReusableHttpServletRequest extends HttpServletRequestWrapper {

    private final byte[] requestBody;

    public ReusableHttpServletRequest(HttpServletRequest httpServletRequest) throws IOException {
        super(httpServletRequest);
        InputStream inputStream = super.getInputStream();
        requestBody = IOUtils.toByteArray(inputStream);
    }

    @Override
    public ServletInputStream getInputStream() {
        return new ReusableHttpServletRequestInputStream(new ByteArrayInputStream(requestBody));
    }

    @Override
    public BufferedReader getReader() throws IOException {
        String characterEncoding = Objects.requireNonNullElse(getCharacterEncoding(), "UTF-8");
        return new BufferedReader(new InputStreamReader(getInputStream(), characterEncoding));
    }

    protected static class ReusableHttpServletRequestInputStream extends ServletInputStream {

        private final InputStream inputStream;

        public ReusableHttpServletRequestInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

// ...остальные методы пустые

Решение

Мой отладчик показал, что это решение несовместимо с MultipartHttpServletRequest, который требуется для методов контроллера с параметрами @RequestPart. В конечном итоге я пришел к очень простому решению:

public class ReusableHttpServletRequest extends StandardMultipartHttpServletRequest {

StandardMultipartHttpServletRequest правильно обрабатывает multipart/form-data и расширяет HttpServletRequestWrapper, который я использовал ранее.

🎈 Теперь я не знаю, считается ли плохим тоном отвечать на свой собственный вопрос, но я изнурял себя почти два дня, пытаясь выяснить, что не так, когда все говорило мне, что мой код контроллера должен работать. Надеюсь, моё решение сэкономит кому-то еще часы жизни.

Ответ или решение

Для устранения исключения MissingServletRequestPartException: Required part 'file' is not present при загрузке файлов в Spring Boot API, необходимо обратить внимание на несколько ключевых моментов.

Проблема

Вы столкнулись с исключением, указывающим на то, что часть запроса с именем ‘file’ отсутствует, несмотря на то, что вы уверены, что загружаете файл с этим именем. Причиной данной проблемы может быть неправильная обработка multipart-запросов.

Причины

  1. Неправильная реализация HttpServletRequest: Ваша кастомная реализация HttpServletRequest может не поддерживать корректную обработку multipart-запросов. Это особенно важно, когда вы работаете с аннотацией @RequestPart, которая требует, чтобы запрашиваемая часть была доступна в виде MultipartHttpServletRequest.

  2. Отсутствие настройки для обработки многосоставных запросов: Убедитесь, что необходимые компоненты для обработки загрузки файлов (например, MultipartResolver) настроены и активированы в вашем приложении.

Решение

После детального изучения вашего кода я обнаружил, что основная проблема заключалась в кастомной реализации HttpServletRequest. Вот шаги к исправлению:

  1. Изменение класса: Вместо расширения HttpServletRequestWrapper, используйте StandardMultipartHttpServletRequest, который уже включает в себя необходимые механизмы для обработки multipart-запросов.

    Вот как выглядит коррекция:

    public class ReusableHttpServletRequest extends StandardMultipartHttpServletRequest {
       public ReusableHttpServletRequest(HttpServletRequest httpServletRequest) throws IOException {
           super(httpServletRequest);
       }
    }
  2. Проверка конфигурации приложения: Убедитесь, что у вас правильно настроены зависимости для обработки multipart-запросов в application.properties:

    spring.servlet.multipart.enabled=true
    spring.servlet.multipart.max-file-size=10MB
    spring.servlet.multipart.max-request-size=10MB
  3. Проверка клиента: Убедитесь, что клиент, отправляющий файл, действительно использует правильный формат multipart/form-data и что файл передается с именем ‘file’. Это можно проверить с помощью инструментов, таких как Postman, или через клиентский код, который вы используете.

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

Заключение

Ваша проблема должна быть решена с помощью упомянутых рекомендаций. Обработка multipart-запросов требует тщательного обращения и правильно настроенной инфраструктуры, особенно если вы используете кастомные решения. Надеюсь, это поможет вам избежать подобных проблем в будущем.

Оцените материал
Добавить комментарий

Капча загружается...