如何使用多部分/表单和分块编码在 spring mvc 中接收文件上传?
Posted
技术标签:
【中文标题】如何使用多部分/表单和分块编码在 spring mvc 中接收文件上传?【英文标题】:How do I receive a file upload in spring mvc using both multipart/form and chunked encoding? 【发布时间】:2013-12-08 08:57:05 【问题描述】:我正在尝试编写一个 spring mvc 方法,该方法可以接收多部分/表单或传输编码分块文件上传。我可以编写一个单独的方法来处理每种类型,但我想用相同的方法来做,所以我可以使用相同的 REST POST uri,例如:
http://host:8084/attachments/testupload
这是我迄今为止最好的尝试:
@RequestMapping(value = "/testupload" , method = RequestMethod.POST, produces =
"application/json")
public @ResponseBody
ResponseEntity<MessageResponseModel> testUpload(
@RequestParam(value = "filedata", required = false) MultipartFile filedata,
final HttpServletRequest request) throws IOException
InputStream is = null;
if (filedata == null)
is = request.getInputStream();
else
is = filedata.getInputStream();
byte[] bytes = IOUtils.toByteArray(is);
System.out.println("read " + bytes.length + " bytes.");
return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK);
使用上述方法我可以上传一个多部分文件,但是如果我上传一个分块文件,我会从 spring 中得到一个异常:
org.springframework.web.multipart.MultipartException: \
The current request is not a multipart request
如果我删除 MultipartFile 请求参数,它非常适合传输编码分块。如果我把它留在里面,它非常适合 MultipartFile 上传。如何使用相同的方法来处理两种上传类型?
这适用于分块:
@RequestMapping(value = "/testupload" , method = RequestMethod.POST, produces =
"application/json")
public @ResponseBody
ResponseEntity<MessageResponseModel> testUpload(
final HttpServletRequest request) throws IOException
InputStream is = null;
is = request.getInputStream();
byte[] bytes = IOUtils.toByteArray(is);
System.out.println("read " + bytes.length + " bytes.");
return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK);
这对 MultipartFile 非常有用:
@RequestMapping(value = "/testupload" , method = RequestMethod.POST, produces =
"application/json")
public @ResponseBody
ResponseEntity<MessageResponseModel> testUpload(
@RequestParam MultipartFile filedata) throws IOException
InputStream is = null;
is = filedata.getInputStream();
byte[] bytes = IOUtils.toByteArray(is);
System.out.println("read " + bytes.length + " bytes.");
return new ResponseEntity<MessageResponseModel>(null, null, HttpStatus.OK);
应该是可以的,有人知道怎么做吗?
谢谢, 史蒂夫
【问题讨论】:
你想要一个端点,并且不介意有两个控制器方法吗?或者你想要单个端点和单个控制器方法? 如果有办法让 2 个控制器方法使用相同的 URI,并且 spring 可以根据是否存在多部分内容来选择调用哪个方法。 【参考方案1】:摘自我的代码(Spring 3.2,使用 AngularJS 上传 blueimp 文件):
/**
* Handles chunked file upload, when file exceeds defined chunked size.
*
* This method is also called by modern browsers and IE >= 10
*/
@RequestMapping(value = "/content-files/upload/", method = RequestMethod.POST, headers = "content-type!=multipart/form-data")
@ResponseBody
public UploadedFile uploadChunked(
final HttpServletRequest request,
final HttpServletResponse response)
request.getHeader("content-range");//Content-Range:bytes 737280-819199/845769
request.getHeader("content-length"); //845769
request.getHeader("content-disposition"); // Content-Disposition:attachment; filename="Screenshot%20from%202012-12-19%2017:28:01.png"
request.getInputStream(); //actual content.
//Regex for content range: Pattern.compile("bytes ([0-9]+)-([0-9]+)/([0-9]+)");
//Regex for filename: Pattern.compile("(?<=filename=\").*?(?=\")");
//return whatever you want to json
return new UploadedFile();
/**
* Default Multipart file upload. This method should be invoked only by those that do not
* support chunked upload.
*
* If browser supports chunked upload, and file is smaller than chunk, it will invoke
* uploadChunked() method instead.
*
* This is instead a fallback method for IE <=9
*/
@RequestMapping(value = "/content-files/upload/", method = RequestMethod.POST, headers = "content-type=multipart/form-data")
@ResponseBody
public HttpEntity<UploadedFile> uploadMultipart(
final HttpServletRequest request,
final HttpServletResponse response,
@RequestParam("file") final MultipartFile multiPart)
//handle regular MultipartFile
// IE <=9 offers to save file, if it is returned as json, so set content type to plain.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new HttpEntity<>(new UploadedFile(), headers);
这应该可以帮助您入门。在 IE8、IE9、IE10、Chrome、FF 上进行的测试最少。当然可能存在问题,并且可能有一种更简单的提取内容范围的方法,但是 .. 对我有用。
【讨论】:
很棒的解决方案!一旦我完成、签入并清理到足以在此处发布的解决方案,我也会发布我的解决方案。不过你的解决方案更好。 我无法使用第一种方法让 blueimp(使用 AngularJS)上传分块。它总是调用uploadMultipart(通过发送多部分标头)。请问有什么特别的 blueimp 配置可以分享吗? 据我记忆,没什么特别的。你使用的是什么浏览器?将哪些标头发送到服务器端?【参考方案2】:这是控制器
package com.faisalbhagat.web.controller;
@Controller
@RequestMapping(value = "" )
public class UploadController
@RequestMapping(value = "/uploadMyFile", method = RequestMethod.POST)
@ResponseBody
public String handleFileUpload(MultipartHttpServletRequest request)
throws Exception
Iterator<String> itrator = request.getFileNames();
MultipartFile multiFile = request.getFile(itrator.next());
try
// just to show that we have actually received the file
System.out.println("File Length:" + multiFile.getBytes().length);
System.out.println("File Type:" + multiFile.getContentType());
String fileName=multiFile.getOriginalFilename();
System.out.println("File Name:" +fileName);
String path=request.getServletContext().getRealPath("/");
//making directories for our required path.
byte[] bytes = multiFile.getBytes();
File directory= new File(path+ "/uploads");
directory.mkdirs();
// saving the file
File file=new File(directory.getAbsolutePath()+System.getProperty("file.separator")+picture.getName());
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(file));
stream.write(bytes);
stream.close();
catch (Exception e)
// TODO Auto-generated catch block
e.printStackTrace();
throw new Exception("Error while loading the file");
return toJson("File Uploaded successfully.")
public String toJson(Object data)
ObjectMapper mapper=new ObjectMapper();
StringBuilder builder=new StringBuilder();
try
builder.append(mapper.writeValueAsString(data));
catch (Exception e)
// TODO Auto-generated catch block
e.printStackTrace();
return builder.toString();
您可以使用客户端代码在以下位置找到完整的解决方案 http://faisalbhagat.blogspot.com/2014/09/springmvc-fileupload-with-ajax-and.html
【讨论】:
我建议不要使用multiFile.getBytes()
,因为它将整个字节数组加载到内存中以上是关于如何使用多部分/表单和分块编码在 spring mvc 中接收文件上传?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring MockMvc 放置多部分/表单数据?
使用 AJAX + 多部分表单数据 + UTF-8 编码发送文件和文本