2

I'm building a REST API using Spring Boot framework. There is one endpoint where users will be able to upload a large file (~1GB). I'm using Streaming API from Apache Commons FileUpload.

I only want to enable streaming only at that endpoint. Therefore, I configure my Spring Boot as follow:

spring.servlet.multipart.enabled = true
spring.servlet.multipart.resolve-lazily = true
spring.servlet.multipart.max-file-size = 2GB
spring.servlet.multipart.max-request-size = 2GB

And here is my endpoint:

@PostMapping(path = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> importData(HttpServletRequest request) throws IOException, FileUploadException {
    System.out.println("Streaming...");

    boolean isMultipart = ServletFileUpload.isMultipartContent(request);
    if (!isMultipart) {
        return ResponseEntity.badRequest().build();
    }

    ServletFileUpload upload = new ServletFileUpload();
    FileItemIterator iterStream = upload.getItemIterator(request);
    while (iterStream.hasNext()) {
        System.out.println("Iterating...");

        FileItemStream item = iterStream.next();

        if (!item.isFormField()) {

            String name = item.getFieldName();
            System.out.println("Field name is: " + name);

            try (InputStream uploadedStream = item.openStream();
                 OutputStream out = new FileOutputStream("file.zip")) {

                IOUtils.copy(uploadedStream, out);
            }

        } else {

            try (InputStream stream = item.openStream()) {
                String formFieldName = item.getFieldName();
                String formFieldValue = Streams.asString(stream);
                System.out.println(String.format("Form field found - %s: %s", formFieldName, formFieldValue));
            }
        }
    }

    return ResponseEntity.ok("Data streamed successfully.");
}

The method is called and the code is executed. However, the FileItemIterator is empty, there for the while loop is not executed.

This code works perfectly if I set spring.servlet.multipart.enabled = false. But I don't want to do that because as I said, I don't want to disable Spring Boot Multipart Resolver globally, but only at this endpoint.

So, what is wrong with my code? Why is the FileItemIterator is empty?

Thank you for your help.

Triet Doan
  • 11,455
  • 8
  • 36
  • 69

1 Answers1

4

I solved my problem.

I think the problem is at Spring StandardServletMultipartResolver. When I switch to CommonsMultipartResolver (as configured below), my code works perfectly.

@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setDefaultEncoding("UTF-8");
    resolver.setResolveLazily(true);
    return resolver;
}
Triet Doan
  • 11,455
  • 8
  • 36
  • 69
  • Hi, thanks for your answer. Unfortunately it does not resolve the issue for me. iterStream.hasNext() returns false from the beginning. I am using some Interceptors and Spring Security which maybe mess with the request before it reaches my controller. In your case, did you also have Spring Security and e.g. a LocaleChangeInterceptor in use? Thanks in advance. – yglodt Apr 25 '20 at 09:58
  • Hi, I have Spring Security but no interceptor. – Triet Doan Apr 25 '20 at 11:39
  • Ok, I can confirm that by disabling the interceptors, hasNext() has begun returning true. Thanks for your feedback. – yglodt Apr 25 '20 at 11:43
  • To disable an interceptor for a specific URL, do this: @Override public void addInterceptors(final InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/the/endpoint/you/use/for/upload"); } – yglodt Apr 25 '20 at 11:48
  • I'm getting No primary or single unique constructor found for interface javax.servlet.http.HttpServletRequest. Any idea why ? – Jorge Machado Mar 14 '22 at 08:46
  • Sorry, but I don't have any experience with that error yet. – Triet Doan Mar 14 '22 at 09:53