spring SpringBoot:使用 Apache Commons FileUpload 上传大型流文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32782026/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:47:48  来源:igfitidea点击:

SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload

springspring-bootapache-commonsapache-commons-fileupload

提问by balajeerc

Am trying to upload a large file using the 'streaming' Apache Commons File Upload API.

我正在尝试使用“流式”Apache Commons File Upload API 上传大文件。

The reason I am using the Apache Commons File Uploader and not the default Spring Multipart uploader is that it fails when we upload very large file sizes (~2GB). I working on a GIS application where such file uploads are pretty common.

我使用 Apache Commons File Uploader 而不是默认的 Spring Multipart Uploader 的原因是当我们上传非常大的文件大小 (~2GB) 时它会失败。我正在开发一个 GIS 应用程序,其中此类文件上传非常普遍。

The full code for my file upload controller is as follows:

我的文件上传控制器的完整代码如下:

@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;
    }

}

The trouble is that the getItemIterator(request)always returns an iterator that does not have any items (i.e. iter.hasNext()) always returns false.

问题是getItemIterator(request)总是返回一个没有任何项的迭代器(即iter.hasNext())总是返回false

My application.properties file is as follows:

我的 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

The JSP view for the /uploaderis as follows:

的 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>

What might I be doing wrong?

我可能做错了什么?

回答by balajeerc

Thanks to some very helpful comments by M.Deinum, I managed to solve the problem. I have cleaned up some of my original post and am posting this as a complete answer for future reference.

感谢 M.Deinum 的一些非常有用的评论,我设法解决了这个问题。我已经清理了我的一些原始帖子,并将其作为完整的答案发布以供将来参考。

The first mistake I was making was not disabling the default MultipartResolverthat Spring provides. This ended up in the resolver processing the HttpServeletRequestand thus consuming it before my controller could act on it.

我犯的第一个错误是没有禁用MultipartResolverSpring 提供的默认值。这最终在解析器处理HttpServeletRequest并因此在我的控制器可以对其采取行动之前消耗它。

The way to disable it, thanks to M. Deinum was as follows:

感谢 M. Deinum 的禁用方法如下:

multipart.enabled=false

However, there was still another hidden pitfall waiting for me after this. As soon as I disabled default multipart resolver, I started getting the following error when trying to make an upload:

然而,在这之后还有一个隐藏的陷阱在等着我。一旦我禁用了默认的多部分解析器,我在尝试上传时开始收到以下错误:

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

In my security configuration, I had enabled CSRF protection. That necessitated that I send my POST request in the following manner:

在我的安全配置中,我启用了 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>

I also modified my controller a bit:

我还稍微修改了我的控制器:

@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;
    }
}

where Response is just a simple generic response type I use:

其中 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
    ...
}

回答by ThetaSinner

If you're using a recent version of spring boot (I'm using 2.0.0.M7) then the property names have changed. Spring started using technology specific names

如果您使用的是最新版本的 Spring Boot(我使用的是 2.0.0.M7),那么属性名称已更改。Spring 开始使用技术特定名称

spring.servlet.multipart.maxFileSize=-1

spring.servlet.multipart.maxRequestSize=-1

spring.servlet.multipart.enabled=false

spring.servlet.multipart.maxFileSize=-1

spring.servlet.multipart.maxRequestSize=-1

spring.servlet.multipart.enabled=false

If you're getting StreamClosed exceptions caused by multiple implementations being active, then the last option allows you to disable the default spring implementation

如果由于多个实现处于活动状态而导致 StreamClosed 异常,那么最后一个选项允许您禁用默认的 spring 实现

回答by HopeBing

Please try to add spring.http.multipart.enabled=falsein application.propertiesfile.

请尝试spring.http.multipart.enabled=falseapplication.properties文件中添加。

回答by user9814979

I use kindeditor + springboot. When I use (MultipartHttpServletRequest) request. I could get the file, but I use appeche-common-io:upload.parse(request) the return value is null.

我使用 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();

回答by pranav patil

You Can simply add spring properties:

您可以简单地添加弹簧属性:

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

here my maximum file size is 20000KB, you can change if required.

这里我的最大文件大小是 20000KB,您可以根据需要进行更改。