Вопрос или проблема
У меня есть метод контроллера в 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-запросов.
Причины
-
Неправильная реализация
HttpServletRequest
: Ваша кастомная реализацияHttpServletRequest
может не поддерживать корректную обработку multipart-запросов. Это особенно важно, когда вы работаете с аннотацией@RequestPart
, которая требует, чтобы запрашиваемая часть была доступна в видеMultipartHttpServletRequest
. -
Отсутствие настройки для обработки многосоставных запросов: Убедитесь, что необходимые компоненты для обработки загрузки файлов (например,
MultipartResolver
) настроены и активированы в вашем приложении.
Решение
После детального изучения вашего кода я обнаружил, что основная проблема заключалась в кастомной реализации HttpServletRequest
. Вот шаги к исправлению:
-
Изменение класса: Вместо расширения
HttpServletRequestWrapper
, используйтеStandardMultipartHttpServletRequest
, который уже включает в себя необходимые механизмы для обработки multipart-запросов.Вот как выглядит коррекция:
public class ReusableHttpServletRequest extends StandardMultipartHttpServletRequest { public ReusableHttpServletRequest(HttpServletRequest httpServletRequest) throws IOException { super(httpServletRequest); } }
-
Проверка конфигурации приложения: Убедитесь, что у вас правильно настроены зависимости для обработки multipart-запросов в
application.properties
:spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB
-
Проверка клиента: Убедитесь, что клиент, отправляющий файл, действительно использует правильный формат
multipart/form-data
и что файл передается с именем ‘file’. Это можно проверить с помощью инструментов, таких как Postman, или через клиентский код, который вы используете. -
Тестирование: После внесения изменений протестируйте ваш контроллер, чтобы убедиться, что он корректно обрабатывает загрузку файла.
Заключение
Ваша проблема должна быть решена с помощью упомянутых рекомендаций. Обработка multipart-запросов требует тщательного обращения и правильно настроенной инфраструктуры, особенно если вы используете кастомные решения. Надеюсь, это поможет вам избежать подобных проблем в будущем.