SpringBoot:使用Apache Commons FileUpload上传大型流式文件
SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload
我正在尝试使用 'streaming' Apache Commons 文件上传 API.
上传大文件
我使用 Apache Commons 文件上传器而不是默认的 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;
}
}
问题是 getItemIterator(request)
总是 returns 一个没有任何项目的迭代器(即 iter.hasNext()
)总是 returns 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
/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 的一些非常有用的评论,我设法解决了这个问题。我已经清理了我原来的一些 post 并将其作为完整答案 post 以供将来参考。
我犯的第一个错误是没有禁用 Spring 提供的默认值 MultipartResolver
。这最终导致解析器处理 HttpServeletRequest
并因此在我的控制器可以对其执行操作之前消耗它。
禁用它的方法,感谢 M.Deinum 如下:
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
...
}
请尝试在 application.properties 文件中添加 spring.http.multipart.enabled=false
。
如果您使用的是最新版本的 spring 引导(我使用的是 2.0.0.M7),那么 属性 名称已更改。
Spring 开始使用技术特定名称
spring.servlet.multipart.maxFileSize=-1
spring.servlet.multipart.maxRequestSize=-1
spring.servlet.multipart.enabled=false
如果您收到由多个实现激活导致的 StreamClosed 异常,则最后一个选项允许您禁用默认 spring 实现
我用的是kindeditor+springboot。当我使用 (MultipartHttpServletRequest) 请求时。我可以获得该文件,但我使用 appeche-common-io:upload.parse(request) return 值为空。
public BaseResult uploadImg(HttpServletRequest request,String type){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();
您可以简单地添加 spring 属性:
spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB
这里我的最大文件大小是20000KB,如果需要你可以改变。
我正在尝试使用 'streaming' Apache Commons 文件上传 API.
上传大文件我使用 Apache Commons 文件上传器而不是默认的 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;
}
}
问题是 getItemIterator(request)
总是 returns 一个没有任何项目的迭代器(即 iter.hasNext()
)总是 returns 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
/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 的一些非常有用的评论,我设法解决了这个问题。我已经清理了我原来的一些 post 并将其作为完整答案 post 以供将来参考。
我犯的第一个错误是没有禁用 Spring 提供的默认值 MultipartResolver
。这最终导致解析器处理 HttpServeletRequest
并因此在我的控制器可以对其执行操作之前消耗它。
禁用它的方法,感谢 M.Deinum 如下:
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
...
}
请尝试在 application.properties 文件中添加 spring.http.multipart.enabled=false
。
如果您使用的是最新版本的 spring 引导(我使用的是 2.0.0.M7),那么 属性 名称已更改。 Spring 开始使用技术特定名称
spring.servlet.multipart.maxFileSize=-1
spring.servlet.multipart.maxRequestSize=-1
spring.servlet.multipart.enabled=false
如果您收到由多个实现激活导致的 StreamClosed 异常,则最后一个选项允许您禁用默认 spring 实现
我用的是kindeditor+springboot。当我使用 (MultipartHttpServletRequest) 请求时。我可以获得该文件,但我使用 appeche-common-io:upload.parse(request) return 值为空。
public BaseResult uploadImg(HttpServletRequest request,String type){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();
您可以简单地添加 spring 属性:
spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB
这里我的最大文件大小是20000KB,如果需要你可以改变。