У нас есть проект, работающий на Undertow & SpringBoot и пытается добавить файлы. Первые попытки были успешными, файлы были привязаны к соответствующим компонентам, используя StandardServletMultipartResolver
и настраивая его с помощью application.properties
. Однако мы столкнулись с ужасными трудностями, когда дело дошло до Error Handling. Мы нашли «решение», настроив стандартный преобразователь на 100 МБ и используя CommonsMultipartResolver
. Затем мы добавили фильтр, как этотЗагрузка файла и допустимая обработка ошибок в Undertow/Wildfly wth Весенняя загрузка
@Bean
public Filter filter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (ServletException e) {
if (e.getCause()
.getClass()
.equals(org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException.class)) {
int requestSize = request.getContentLength();
Collection<Part> parts = request.getParts();
List<String> oversizedFields = new LinkedList<>();
long uploadSize = 0;
for (Part part : new ArrayList<>(parts)) {
if (uploadSize + part.getSize() > MAX_UPLOAD_SIZE) {
requestSize -= part.getSize();
oversizedFields.add(part.getName());
request.getParameterMap()
.remove(part.getName());
parts.remove(part);
} else {
uploadSize += part.getSize();
}
}
request.setAttribute("oversizedFields", oversizedFields);
SizeModifyingServletRequestWrapper requestWrapper = new SizeModifyingServletRequestWrapper(
request, requestSize, uploadSize);
filterChain.doFilter(requestWrapper, response);
}
}
}
};
}
Requestwrapper:
private static class SizeModifyingServletRequestWrapper extends
HttpServletRequestWrapper {
private int size;
private long sizeLong;
public SizeModifyingServletRequestWrapper(HttpServletRequest request,
int size, long sizeLong) {
super(request);
this.size = size;
this.sizeLong = sizeLong;
}
@Override
public int getContentLength() {
return size;
}
@Override
public long getContentLengthLong() {
return sizeLong;
}
@Override
public String getHeader(String name) {
if (FileUploadBase.CONTENT_LENGTH.equals(name)) {
return Integer.toString(size);
} else {
return super.getHeader(name);
}
}
}
@Controller
-метод затем проверяет наличие больших файлов и добавляет результат к BindingResult
, который прекрасно работает, за исключением того, что файлы не привязаны к компоненту. Оказывается, что при попытке разобрать запрос CommonsMultipartResolver
выбрасывает MalformedStreamException
в ItemInputStream.makeAvailable()
, который всегда возвращает сообщение String ended unexpectedly
.
Таким образом, мы вернулись к использованию StandardServletMultipartResolver
и смогли поймать RuntimeException
. Он отлично поддается, однако он не предоставляет абсолютно никаких данных формы, когда даже один файл превышает границы его размера.
Мы абсолютно застопорились, поскольку это неважно, работает ли резольвер лениво или нет. Если у кого есть какие-либо дополнительные идеи, как решить этот вопрос, то можете предложить ответы =)
Далее Код для ссылки:
Выписка из WebAppInitializer
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(true);
return multipartResolver;
}
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize("2MB");
factory.setMaxRequestSize("100MB");
return factory.createMultipartConfig();
}
Выписка из контроллера:
@RequestMapping(method = { RequestMethod.POST, RequestMethod.PUT })
public String saveOrganizationDetails(
@PathVariable(PATH_VARIABLE_ORGANIZATION_ID) String organizationId,
@ModelAttribute @Valid Organization organization,
BindingResult bindingResult, Model model,
RedirectAttributes redirectAttributes, WebRequest request) {
checkForOversizedFiles(request, bindingResult);
Map<String, MultipartFile> files = organization.getStyle().whichFiles();
}
private boolean checkForOversizedFiles(WebRequest request,
BindingResult bindingResult) {
if (request.getAttribute("oversizedFields", WebRequest.SCOPE_REQUEST) instanceof LinkedList) {
@SuppressWarnings("unchecked")
LinkedList<String> oversizedFiles = (LinkedList<String>) request
.getAttribute("oversizedFields", WebRequest.SCOPE_REQUEST);
for (String s : oversizedFiles) {
String errorCode = KEY_ORGANIZATION_LOGO_OVERSIZED_FILE + s;
bindingResult.rejectValue(s,
errorCode);
}
return true;
} else {
return false;
}
}
private void handleUpload(Map<String, MultipartFile> files,
OrganizationStyle style, BindingResult result) {
for (String filename : files.keySet()) {
if (processUpload(files.get(filename), filename)) {
style.setLogoFlag(filename);
} else {
result.reject(KEY_ORGANIZATION_LOGO_UPLOAD_FAILURE);
}
}
}
processUpload()
до сих пор не имеет функциональности, поэтому я не включаю его здесь.
Извлечение из формы щиту Bean:
public class OrganizationStyle {
@Transient
private MultipartFile logoPdf;
@Transient
private MultipartFile logoCustomerArea;
@Transient
private MultipartFile logoAssistant;
@Transient
private MultipartFile logoIdentityArea;
<omitting Getters and setters>
private Map<String, MultipartFile> getAllFiles() {
Map<String, MultipartFile> files = new HashMap<>();
files.put("logoPdf", logoPdf);
files.put("logoCustomerArea", logoCustomerArea);
files.put("logoAssistant", logoAssistant);
files.put("logoIdentityArea", logoIdentityArea);
return files;
}
public Map<String, MultipartFile> whichFiles() {
Map<String, MultipartFile> whichFiles = new HashMap<>();
for (String name : getAllFiles().keySet()) {
MultipartFile file = getAllFiles().get(name);
if (file != null && !file.isEmpty()) {
whichFiles.put(name, file);
}
}
return whichFiles;
}
}
Это, как говорится, не весь код, но необходимый код для этой конкретной проблемы. Исключение брошено при загрузке больших файлов или:
(java.io.IOException) java.io.IOException: UT000054: The maximum size 2097152 for an individual file in a multipart request was exceeded
или упомянутый FileUploadBase.FileSizeLimitExceedeException
И последнее, но не в последнюю очередь, экстракт форм-страницы
<div id="layoutOne" class="panel-collapse collapse">
<div class="panel-body">
<div class="form-group">
<label for="logoPdf" class="control-label" th:text="#{organizationcontext.groups.addmodal.logo.form.label}">LOGO-FORM</label>
<input type="file" th:field="*{style.logoPdf}" accept="image/*" />
</div>
<div class="form-group">
<label for="logoCustomerArea" class="control-label" th:text="#{organizationcontext.groups.addmodal.logo.customer.label}">LOGO-ORGANIZATION</label>
<input type="file" th:field="*{style.logoCustomerArea}" accept="image/*" />
</div>
<div class="form-group">
<label for="logoAssistant" class="control-label" th:text="#{organizationcontext.groups.addmodal.logo.assistant.label}">LOGO-ASSISTANT</label>
<input type="file" th:field="*{style.logoAssistant}" accept="image/*" />
</div>
<div class="form-group">
<label for="logoIdentityArea" class="control-label" th:text="#{organizationcontext.groups.addmodal.logo.id.label}">LOGO-ID</label>
<input type="file" th:field="*{style.logoIdentityArea}" accept="image/*" />
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('style.cssUrl')}? has-error">
<label for="style.cssUrl" class="control-label" th:text="#{organizationcontext.groups.addmodal.css.external.label}">CSS-EXTERNAL</label>
<input th:field="*{style.cssUrl}" class="form-control" type="text" th:placeholder="#{placeholder.css.external}" />
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('style.cssCode')}? has-error">
<label for="style.cssCode" class="control-label" th:text="#{organizationcontext.groups.addmodal.css.input.label}">CSS</label>
<textarea th:field="*{style.cssCode}" class="form-control" th:placeholder="#{placeholder.css.input}"></textarea>
</div>
</div>
</div>
Если у вас вы должны были понять, что мы уже пробовали несколько возможных решений, большинство из которых были здесь.Прямо сейчас, фильтр не ловит RuntimeException
и чеки IOException
в качестве причины, а также, размеры больше не устанавливается в пределах application.properties
Любая помощь или предложения вообще были бы очень признательны.
Дополнительная информация
Итак, я отладил StandardServletMultipartResolver
и обнаружил, что он использует ISO-8859-1-кодовую для его анализа. Это дает желаемые эффекты, даже если страницы кодируются в кодировке UTF-8, а объект запроса также имеет UTF-8-Charset. Я пытался заставить ISO-Charset с фильтром, как так
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public Filter characterEncodingFilter() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("ISO-8859-1");
characterEncodingFilter.setForceEncoding(true);
return characterEncodingFilter;
}
, но по какой-то причине, CommonsMultipartResolver
находит UTF-8 закодированные объект запроса, так как эта кодировка не работает, или у меня есть сделала еще одну ошибку, которую я не вижу.
Я также попытался найти точный момент выброшенного исключения, возможно, сам расширить класс и убедиться, что уже разрешенные данные формы сохранены, до сих пор безрезультатно.
Еще больше информации
Как предложено другой нитью здесь, я пытался заставить ISO-8859-1 по запросу. Сначала это полностью обходило CommonsMultipartResolver
и испортило мой текст, теперь он фильтрует правильный резольвер, но в нем все еще говорится, что в многостраничных данных нет файлов. Просто для справки, Фильтровальный я использовал:
private class MyMultiPartFilter extends MultipartFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setCharacterEncoding("ISO-8859-1");
request.getParameterNames();
super.doFilterInternal(request, response, filterChain);
}
}
Сделано Bean из него и изменил название multipartResolver() - Фасоль в filterMultipartResolver()