SpringBoot:使用ApacheCommonsFileUpload上传大型流文件

SpringBoot:使用ApacheCommonsFileUpload上传大型流文件,spring,spring-boot,apache-commons,apache-commons-fileupload,Spring,Spring Boot,Apache Commons,Apache Commons Fileupload,我正在尝试使用“流式”Apache Commons文件上载API上载一个大文件 我之所以使用apachecommons文件上传程序而不是默认的Spring多部分上传程序,是因为当我们上传非常大的文件大小(~2GB)时,它会失败。我正在开发一个GIS应用程序,这种文件上传非常常见 我的文件上载控制器的完整代码如下: @Controller public class FileUploadController { @RequestMapping(value="/upload", method

我正在尝试使用“流式”Apache Commons文件上载API上载一个大文件

我之所以使用apachecommons文件上传程序而不是默认的Spring多部分上传程序,是因为当我们上传非常大的文件大小(~2GB)时,它会失败。我正在开发一个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;
    }

}
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
<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>
multipart.enabled=false
问题是
getItemIterator(request)
总是返回一个没有任何项的迭代器(即
iter.hasNext()
)总是返回
false

我的application.properties文件如下:

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

}
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
<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>
multipart.enabled=false
/uploader
的JSP视图如下所示:

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

}
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
<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>
multipart.enabled=false

要上载的文件:
名称:

按这里上传文件!

我可能做错了什么?

多亏了迪纳姆先生的一些非常有用的评论,我成功地解决了这个问题。我已经清理了我的一些原始帖子,并将此作为一个完整的答案发布,以供将来参考

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

多亏了M.Deinum,禁用它的方法如下:

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

}
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
<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>
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保护。因此,我必须以以下方式发送我的邮寄请求:

<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;
    }
}
@控制器
公共类FileUploadController{
@RequestMapping(value=“/upload”,method=RequestMethod.POST)
public@ResponseBody响应上传(HttpServletRequest请求){
试一试{
布尔值isMultipart=ServletFileUpload.isMultipartContent(请求);
如果(!isMultipart){
//通知用户无效的请求
Response responseObject=新响应(false,“不是多部分请求。”,“”);
返回响应对象;
}
//创建新的文件上载处理程序
ServletFileUpload upload=新建ServletFileUpload();
//解析请求
FileItemIterator iter=upload.getItemIterator(请求);
while(iter.hasNext()){
FileItemStream item=iter.next();
字符串名称=item.getFieldName();
InputStream=item.openStream();
如果(!item.isFormField()){
字符串文件名=item.getName();
//处理输入流
OutputStream out=新文件OutputStream(文件名);
IOUtils.copy(流,输出);
stream.close();
out.close();
}
}
}捕获(文件上载异常){
返回新的响应(false,“文件上载错误”,例如toString());
}捕获(IOE异常){
返回新的响应(false,“内部服务器IO错误”,例如toString());
}
返回新的响应(true,“Success”和“”);
}
@RequestMapping(value=“/uploader”,method=RequestMethod.GET)
公共模型和视图上载页面(){
ModelAndView模型=新的ModelAndView();
model.setViewName(“上传器”);
收益模型;
}
}
其中响应只是我使用的一种简单的通用响应类型:

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
    ...
}
公共类响应{
/**指示请求是否成功的布尔值**/
私有布尔状态;
/**指示错误(如果有)的消息**/
私有字符串消息;
/**作为此响应一部分的其他数据**/
私有T数据;
公共响应(布尔状态、字符串消息、T数据){
这个状态=状态;
this.message=消息;
这个数据=数据;
}
//二传手和接球手
...
}

请尝试在application.properties文件中添加
spring.http.multipart.enabled=false

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

spring.servlet.multipart.maxFileSize=-1

spring.servlet.multipart.maxRequestSize=-1

spring.servlet.multipart.enabled=false


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

我使用kindeditor+springboot。当我使用(multipartTTPServletRequest)请求时。我可以获取该文件,但我使用appeche common io:upload.parse(request)返回值为null

public BaseResult uploadImg(HttpServletRequest request,String type){
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
                MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();
public BaseResult uploadImg(HttpServletRequest请求,字符串类型){
MultipartTTpServletRequest multipartRequest=(MultipartTTpServletRequest)请求;
MultiValueMap multiFileMap=multipartRequest.getMultiFileMap();

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

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

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

是否已禁用springs多部分支持,否则您的解决方案将无法工作,Spring将已解析请求。使用单个
multipart.enabled=false来替换所有
multipart
属性以禁用默认处理。我没有做任何事情ng特定于禁用spring多部分支持。我尝试在我的
应用程序.properties
文件中添加
multipart.enabled=false
。但是,一旦这样做,我每次上传时都会收到一个
405:Request方法“POST”不受支持的错误。这表明映射错误或发布到错误的url