前言
无论何种编程语言,文件下载的原理都是一样的,将文件以流的方式写出到response中,设置http响应报文Content-Type
(浏览器据此识别文件类型)、Content-Disposition
(浏览器据此知道是下载文件而不是打开文件),浏览器能知道是下载文件,所以即使是默认表单提交,页面也不会刷新;同理,超链接、js提交表单页面都不会刷新;
代码
效果图
html页面
<div>
<form action="/download/do" id="downloadform">
<input type="submit" value="submit下载">
<input type="button" value="btn下载" onclick="download()">
<a href="/download/do">超链接下载文件</a>
</form>
</div>
<script type="text/javascript">
function download(){
var form=document.getElementById("downloadform");
form.submit();
}
</script>
后台代码
后台代码有两个关键点,第一设置Content-Type、第二设置Content-Disposition
- Content-Type
- 用来告诉浏览器的文件类型,因为要下载excel所以使用
application/vnd.ms-excel
- 用来告诉浏览器的文件类型,因为要下载excel所以使用
- Content-Dispostion
- 用来告诉浏览器是下载而不是打开,disposition[ˌdɪspəˈzɪʃn] n.意向,通过英语可知这是告知内容意向的,表达的很恰当,细心的读者也许会发现,上传文件的multipart-data数据格式也用到了Content-Disposition,言归正传,下载时的格式
Content-Dispostion: attachment; filename=filename.suffix
@Controller
@RequestMapping("download")
public class DownloadController {
//因为输出流只能写出一次,所以,文件下载的后台方法,都是无返回值
@RequestMapping("do")
public void download(HttpServletResponse response) throws IOException{
String generateFileName = "我的文件.txt";
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename="
+ URLEncoder.encode(generateFileName,"utf8"));
response.getOutputStream().write("你好".getBytes());
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
下载其他类型文件
那有人要问了,这是下载excel,如果下载csv、pdf,该怎么办?
查看MIME表,将Content-Type改为对应类型即可,比如csv是text/csv
、pdf是application/pdf
。
只打开不下载
很明显,需要调整Content-Dispostion,去掉Content-Dispostion即可。
常见误解
最开始做时,我以为用表单提交方式下载文件,页面肯定很刷新,并且网上也有很多实用iframe方式来规避页面刷新的方案,但是通过测试发现,如果设置Content-Type
合适,不会出现页面刷新情况;所以,这里也纠正一下;
通过这个问题,我们可以联想到文件上传,文件上传后返回的Content-Type
不是下载文件类型,所以,页面肯定会刷新,所以,才需要使用iframe来规避页面刷新问题,关于页面无刷新实现文件上传,请参考form+iframe无刷新上传文件;
总结
- 文件下载最重要的两个点是Content-Type、Content-Dispostion,弄懂了这两个,打开而不下载文件、下载其他类型文件就手到擒来了。
- 文件下载是个很常用的功能,但每次都是百度复制,没有弄懂,弄透彻,汗颜。