SpringBoot:使用Apache Commons FileUpload的大型流文件上传 [英] SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload

查看:234
本文介绍了SpringBoot:使用Apache Commons FileUpload的大型流文件上传的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正在尝试使用流式" Apache Commons File Upload API上传大文件.

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

我使用Apache Commons File Uploader而不是默认的Spring Multipart Uploader的原因是,当我们上传非常大的文件大小(〜2GB)时,它失败了.我在GIS应用程序上工作,这种文件上传非常常见.

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.

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

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

}

麻烦之处在于getItemIterator(request)总是返回没有任何项目的迭代器(即iter.hasNext())总是返回false.

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

我的application.properties文件如下:

My application.properties file is as follows:

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

/uploader的JSP视图如下:

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

我可能做错了什么?

推荐答案

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

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.

我犯的第一个错误是没有禁用Spring提供的默认MultipartResolver.这最终导致解析器处理HttpServeletRequest,并在我的控制器对其执行操作之前将其消耗掉.

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

禁用它的方法,这要感谢M. Deinum:

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

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

在我的安全配置中,我启用了CSRF保护.这就需要我以以下方式发送POST请求:

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

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

其中Response只是我使用的一种简单的通用响应类型:

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

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

这篇关于SpringBoot:使用Apache Commons FileUpload的大型流文件上传的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆