内容参考 Paul Deck 著的《Spring MVC学习指南1 2版》
将文件这样的资源发送到浏览器,需要在controller中完成以下工作:
- 在请求的处理方法中使用void返回类型,并在方法中添加
HttpServletResponse
- 设置
Content-Type
, Content-Type就是常说的MIME类型,将响应的内容类型设为文件的内容类型。Content-Type
header 在某个实体的body中定义数据类型, 包含media type(媒体类型)和subtype identifiers(子类标识符)。访问http://www.iana.org/assignments/media-types 以了解更多的标准内容类型(standard content types)。如果不清楚如何设置Content-Type,要达到下载资源的效果,可以将Content-Type设置为application/octet-stream
(不区分大小写)。 - 设置一个 HTTP response header:
Content-Disposition
, 值为attachment; filename=fileName
, 这个fileName
就是下载文件后的文件名,这个文件名有可能会和这里设置fileName不一样。
上码:
方式1:读取文件作为FileInputStream
, 将内容加载到一个字节数组。随后获取HttpServletResponse的OutputStream,并调用write方法传入字节数组。
方式2:使用Java NIO's Files.copy()
方法:
Path file = Paths.get(...);
Files.copy(file, response.getOutputStream());
这样代码少、而且运行得更快。
看码吧:
@Controller
public class DownloadController {
@RequestMapping(value="/download-resource")
public void downloadResource(HttpServletRequest servletRequest, HttpServletResponse response) {
// 文件保存在/WEB_INF/data目录下
String dataDirectory = request.
getServletContext().getRealPath("/WEB-INF/data");
File file = new File(dataDirectory, "sunny.pdf");
if (file.exists()) {
response.setContentType("application/pdf");
response.addHeader("Content-Disposition",
"attachment; filename=sunny.pdf");
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
// if using Java 7, use try-with-resources
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
} catch (IOException ex) {
// do something,
// probably forward to an Error page
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
}
}
}
@RequestMapping(value="/download-resource1")
public void downloadResource(HttpServletRequest servletRequest, HttpServletResponse response) {
String dataDirectory = request.
getServletContext().getRealPath("/WEB-INF/data");
Path file = Paths.get(dataDirectory, "sunny.pdf");
if (Files.exists(file)) {
response.setContentType("application/pdf");
String fileName = "sunny.pdf";
response.addHeader("Content-Disposition",
"attachment; filename=" + fileName);
/*
如果文件名有中文的话,进行URL编码,让中文正常显示
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
*/
try {
Files.copy(file, response.getOutputStream());
} catch (IOException ex) {
}
}
}
}
针对中文文件名下载后乱码分析:
上面有说
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
这行代码是能解决中文文件名乱码问题的,但在Safari浏览器下下载的中文文件名还是乱码的,下面提供一个解决方案,看代码:
@RequestMapping(value = "/faq")
public void downloadFAQPdf(HttpServletResponse response) throws Exception {
String fileName = "帮助说明.pdf";
// 获取classpath下的文件
URL url = UserController.class.getClassLoader().getResource("data/" + fileName);
Path file = Paths.get(url.toURI());
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
// 解决中文文件名乱码关键行
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName);
Files.copy(file, response.getOutputStream());
}
具体原因我就不细说了,参看下面这个链接(有详细的描述):
http://ju.outofmemory.cn/entry/133400