Подтвердить что ты не робот

SpringBoot: загрузка большого потокового файла с использованием Apache Commons FileUpload

Я пытаюсь загрузить большой файл с помощью "потокового" API загрузки файлов Apache Commons.

Причина, по которой я использую Apache Commons File Uploader, а не загрузчик по умолчанию Spring Multipart, заключается в том, что он терпит неудачу, когда мы загружаем очень большие размеры файлов (~ 2 ГБ). Я работаю над приложением GIS, где такие загрузки файлов довольно распространены.

Полный код для моего контроллера загрузки файлов выглядит следующим образом:

@Controller
public class FileUploadController {

    @RequestMapping(value="/upload", method=RequestMethod.POST)
    public void upload(HttpServletRequest request) {
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            // Inform user about invalid request
            return;
        }

        //String filename = request.getParameter("name");

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload();

        // Parse the request
        try {
            FileItemIterator iter = upload.getItemIterator(request);
            while (iter.hasNext()) {
                FileItemStream item = iter.next();
                String name = item.getFieldName();
                InputStream stream = item.openStream();
                if (item.isFormField()) {
                    System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
                } else {
                    System.out.println("File field " + name + " with file name " + item.getName() + " detected.");
                    // Process the input stream
                    OutputStream out = new FileOutputStream("incoming.gz");
                    IOUtils.copy(stream, out);
                    stream.close();
                    out.close();

                }
            }
        }catch (FileUploadException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/uploader", method = RequestMethod.GET)
    public ModelAndView uploaderPage() {
        ModelAndView model = new ModelAndView();
        model.setViewName("uploader");
        return model;
    }

}

Проблема в том, что getItemIterator(request) всегда возвращает итератор, у которого нет элементов (т.е. iter.hasNext()), всегда возвращает false.

Мой файл application.properties выглядит следующим образом:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:19095/authdb
spring.datasource.username=georbis
spring.datasource.password=asdf123

logging.level.org.springframework.web=DEBUG

spring.jpa.hibernate.ddl-auto=update

multipart.maxFileSize: 128000MB
multipart.maxRequestSize: 128000MB

server.port=19091

Вид JSP для /uploader выглядит следующим образом:

<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload">
    File to upload: <input type="file" name="file"><br />
    Name: <input type="text" name="name"><br /> <br />
    Press here to upload the file!<input type="submit" value="Upload">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>

Что я могу делать неправильно?

4b9b3361

Ответ 1

Благодаря некоторым очень полезным комментариям М.Дейнума мне удалось решить эту проблему. Я очистил часть своего оригинального сообщения и публикую это как полный ответ для дальнейшего использования.

Первая ошибка, которую я делал, не была отключена по умолчанию MultipartResolver, которую предоставляет Spring. Это закончилось тем, что преобразователь обработал HttpServeletRequest и, таким образом, использовал его, прежде чем мой контроллер мог действовать на него.

Способ отключить его, благодаря М. Дейнуму, был следующим:

multipart.enabled=false

Однако после этого меня ожидала еще одна скрытая ловушка. Как только я отключил многопользовательский преобразователь по умолчанию, я начал получать следующую ошибку при попытке сделать загрузку:

Fri Sep 25 20:23:47 IST 2015
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported

В моей конфигурации безопасности я включил защиту CSRF. Это потребовало, чтобы я отправил свой запрос POST следующим образом:

<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}">
    <input type="file" name="file"><br>
    <input type="submit" value="Upload">
</form>
</body>
</html>

Я также немного изменил свой контроллер:

@Controller
public class FileUploadController {
    @RequestMapping(value="/upload", method=RequestMethod.POST)
    public @ResponseBody Response<String> upload(HttpServletRequest request) {
        try {
            boolean isMultipart = ServletFileUpload.isMultipartContent(request);
            if (!isMultipart) {
                // Inform user about invalid request
                Response<String> responseObject = new Response<String>(false, "Not a multipart request.", "");
                return responseObject;
            }

            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload();

            // Parse the request
            FileItemIterator iter = upload.getItemIterator(request);
            while (iter.hasNext()) {
                FileItemStream item = iter.next();
                String name = item.getFieldName();
                InputStream stream = item.openStream();
                if (!item.isFormField()) {
                    String filename = item.getName();
                    // Process the input stream
                    OutputStream out = new FileOutputStream(filename);
                    IOUtils.copy(stream, out);
                    stream.close();
                    out.close();
                }
            }
        } catch (FileUploadException e) {
            return new Response<String>(false, "File upload error", e.toString());
        } catch (IOException e) {
            return new Response<String>(false, "Internal server IO error", e.toString());
        }

        return new Response<String>(true, "Success", "");
    }

    @RequestMapping(value = "/uploader", method = RequestMethod.GET)
    public ModelAndView uploaderPage() {
        ModelAndView model = new ModelAndView();
        model.setViewName("uploader");
        return model;
    }
}

где Response - просто простой общий тип ответа, который я использую:

public class Response<T> {
    /** Boolean indicating if request succeeded **/
    private boolean status;

    /** Message indicating error if any **/
    private String message;

    /** Additional data that is part of this response **/
    private T data;

    public Response(boolean status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }

    // Setters and getters
    ...
}

Ответ 2

Если вы используете последнюю версию загрузки spring (я использую 2.0.0.M7), имена свойств изменились. Spring начал использовать имена конкретных технологий

spring.servlet.multipart.maxFileSize = -1

spring.servlet.multipart.maxRequestSize = -1

spring.servlet.multipart.enabled = ложь

Если вы получаете исключения StreamClosed, вызванные активностью нескольких реализаций, последний параметр позволяет отключить реализацию spring по умолчанию

Ответ 3

Попробуйте добавить spring.http.multipart.enabled=false в файл application.properties.

Ответ 4

Я использую kindeditor + springboot. Когда я использую (MultipartHttpServletRequest) запрос. Я мог бы получить файл, но я использую appeche-common-io: upload.parse(request), возвращаемое значение равно нулю.

public BaseResult uploadImg(HttpServletRequest request,String type){
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
                MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();

Ответ 5

Вы можете просто добавить свойства пружины:

spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB

здесь мой максимальный размер файла составляет 20000 КБ, вы можете изменить при необходимости.