今天碰到一个比较奇怪的技术问题,使用Spring MVC做文件下载时,FireFox、Chrome浏览器下载都没有遇到问题,IE 11缺不能正常下载,总是提示“可能已删除或移动文件”。大家都说是IE浏览器不支持HTTP CREATED(201)状态码,但事实上我返回的是HTTP OK(200)状态码,但问题依然得不到解决,因此引起此问题的原因跟这个HTTP状态码是无关的。
看看下载到的文件,文件其实已经下载下来了,只不过其文件扩展名为“.w2s1z41.partial”,将扩展名去掉,得到的是一个zip文件,该zip文件可以正常解压缩。因此断定,文件已全部下载完成。
再来看看服务端的问题代码(针对IE不能正常下载,其他浏览器正常):
/**
* 客户端软件的更新下载
* @return
* @throws HttpErrorException
*/
@RequestMapping(value="/client/update", method=RequestMethod.GET)
public ResponseEntity<byte[]> update() throws HttpErrorException {
String zipFile = "E:\\proj-svn-local\\tmp\\PPMS-Client-Setup_x86_1.0.35.zip";
File downloadZip = new File(zipFile);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", downloadZip.getName());
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
ResponseEntity<byte[]> responseEntity;
try {
responseEntity = new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(downloadZip), headers, HttpStatus.OK);
} catch (IOException e) {
modelMsg = "Read zip file error: " + downloadZip.getPath();;
throw new HttpErrorException(modelMsg, HttpStatus.INTERNAL_SERVER_ERROR.value());
}
return responseEntity;
}
经过多番调试,最终发现问题的所在,原来问题出在下面这行上:
headers.setContentDispositionFormData("attachment", downloadZip.getName());
这行代码的作用是用于告诉浏览器如何显示本次请求响应所附加的文件,“attachment”表示是让浏览器以下载附件的形式打开文件。按道理,这也没错。通过调试打印出其设置内容,得到文本如下:
[form-data; name="attachment"; filename="PPMS-Client-Setup_x86_1.0.35.zip"]
问题出现了,前面出现了“form-data”字符,此标识应该是在文件上传时才使用(上传文件时一般都将文件放置在表单域中),而当前要做的是“文件下载”,这样不出问题才怪啊。取而代之的headers.set方法,而非headers.setContentDispositionFormData()方法,所以将问题行代码更换成下述代码,问题便可得到解决:
headers.set("Content-Disposition", "attachment; filename=\"" + downloadZip.getName() + "\"");
解决中文名乱码问题
@RequestMapping("/download/{fileId}")
public ResponseEntity<byte[]> download(HttpServletRequest request,HttpSession httpSession,@PathVariable String fileId) throws IOException {
Map<String, Object> cacheFile = getCacheFileBySession(httpSession, fileId);
System.out.println("encode:"+request.getCharacterEncoding());
//中文文件名支持
String encodedfileName = null;
String agent = request.getHeader("USER-AGENT");
if(null != agent && -1 != agent.indexOf("MSIE")){//IE
encodedfileName = java.net.URLEncoder.encode(((String)cacheFile.get("fileName")),"UTF-8");
}else if(null != agent && -1 != agent.indexOf("Mozilla")){
encodedfileName = java.net.URLEncoder.encode(((String)cacheFile.get("fileName")),"UTF-8");
}else{
encodedfileName = java.net.URLEncoder.encode((String)cacheFile.get("fileName"),"UTF-8");
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//headers.setContentDispositionFormData("attachment", (String)cacheFile.get("fileName"));
headers.set("Content-Disposition", "attachment; filename=\"" + encodedfileName + "\"");
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(new File((String) cacheFile.get("downLoadPath"))),
headers, HttpStatus.OK);
}
tomcat /config/server.xml 添加 URIEncoding="UTF-8"
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
/>