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
SpringBoot: Large Streaming File Upload Using Apache 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 /uploader
is 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 MultipartResolver
that Spring provides. This ended up in the resolver processing the HttpServeletRequest
and thus consuming it before my controller could act on it.
我犯的第一个错误是没有禁用MultipartResolver
Spring 提供的默认值。这最终在解析器处理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=false
in application.propertiesfile.
请尝试spring.http.multipart.enabled=false
在application.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,您可以根据需要进行更改。