① 获取文件输入流
② 设置http响应头的相关属性
③ 获取HttpServletResponse对象的输出流
④ 将输入流的内容写到输出流
文件下载大致可以分为两种形式:下载静态资源、下载服务器上的资源。下面分别所说这两种文件的下载方式。
下载静态资源很简单,只需要将链接路径直接指向项目的静态资源位置就行,浏览器就可以自动下载。不过这里有几个需要注意的地方
- 直接指向静态资源时,如果静态资源的文件名是中文,可能会出现请求乱码,导致文件请求失败的情况。这个问题可以试试配置字符编码过滤器,或者修改Tomcat的配置文件。我暂时没有遇到这个问题,可以参考这些方法,不过这些方法都不太能根本性解决问题,有兴趣的同学可以自己摸索下。
- 还可能会出现这个问题:第一次将静态资源文件复制放到web工程的静态资源文件夹里,然后通过链接路径直接访问下载,会出现404情况,系统找不到指定文件。但是当修改文件名称,第一次能够访问到资源之后,无论将文件名再次修改成什么样,系统都能很正常的访问到。这个问题我目前还是没有找到解决方法,此处留待更新。
- 如果可以的话,最好都使用字节流读取文件,不然当下载大文件(超过500M可能)的时候,可能会导致Tomcat关闭连接,导致系统崩溃
- 如果是文本文件,可以使用字符流(BufferedRead等)读取文件
- 如果是Excel、Word等office办公文件或者其他特殊类型的文件,要使用POI等专用方式来获取文件的输入流,否则会导致下载下来的文件无法打开。
- 如果是媒体文件MP3、电影等,一定要使用字节流(FileInputStream)读取文件,负责会导致文件下载成功但无法播放。
- Content-Type的值设为application/octet-stream;charset=utf-8或者application/x-msdownload;charset=utf-8
- Content-Disposition的值设为attachment;filename=文件名
- Content-Disposition是MIME协议的扩展,MIME协指示MIME用户代理如何显示附加的文件。其实就是可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问的时候弹出文件下载对话框
- Content-Disposition=“Content-Disposition” “:” disposition-type*(";" disposition-param)
- dispostion-type是以什么方式下载,如attachment就是以附件方式下载。
- disposition-param是默认保存时的文件名
- 服务端向客户端浏览器发送文件的时候,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器里打开,如果需要提示用户保存,就使用Content-Disposition进行处理,但是一定要加上attachment
-注意:此处的文件名一定要使用URLEncoder.encode方法重新编码,把文件名设成UTF-8的编码,否则会导致文件下载后无法解析文件,无法打开下载的文件。
- 如果是文本文件,可以将字节输出流包装成字符输出流,这样就可以使用更多的API了。
- 如果是Excel、Word等办公文件或其他特殊格式的文件,要使用POI等专用API来写出,否则会导致下载的文件损坏,无法打开。
- 如果是媒体文件,一定要用字节流写出,否则会导致下载的文件无法打开,无法播放。
- Content-Type的值设为application/octet-stream;charset=utf-8或者application/x-msdownload;charset=utf-8
- Content-Disposition的值设为attachment;filename=文件名
注意:此处的文件名一定要使用URLEncoder.encode方法重新编码,把文件名设成UTF-8的编码,否则会导致文件下载后无法解析文件,无法打开下载的文件。
- 如果是文本文件,可以将字节输出流包装成字符输出流,这样就可以使用更多的API了。
- 如果是Excel、Word等办公文件或其他特殊格式的文件,要使用POI等专用API来写出,否则会导致下载的文件损坏,无法打开。
- 获取文件输入流或生成临时文件
- Content-Type的值设为application/octet-stream;charset=utf-8或者application/x-msdownload;charset=utf-8
- Content-Disposition的值设为attachment;filename=文件名
- 返回一个ResponseEntity对象,HttpStatus使用OK状态,负责会导致个别浏览器无法下载资源。
1)前端代码
<button id="filedownload1" class="layui-btn">文本类型文件下载</button>
<script>
$(document).ready(function () {
/*文本类型文件下载按钮监听器*/
$('#filedownload1').click(function () {
window.location.href="<%=request.getContextPath()%>/test/downloadfile_1";
});
});
</script>
2)后端代码
/**
* 普通I/O流CIA在文件控制器
* 流程:①获取服务器上的文件输入流
* ②设置响应头的相关属性
* ③获取HttpServletResponse对象的字节输出流
* ④将输入流内容写到输出流里面
* @author dalei
* @param response
*/
@GetMapping("/downloadfile_1")
public void fileDownloadController1(HttpServletRequest request, HttpServletResponse response) throws IOException {
/**
* ①获取服务器上的文件输入流
* 因为下载的是文本类型的文件,所以可以使用字符流进行输入,
* 但是最好使用字节流输入,否则大文件可能会下载失败,还可能导致系统崩溃
*/
//获取文件路径,这里需要写文件的绝对路径,相对路径会找不到文件
String filePath="D:\\Java\\idea\\Projects\\MedicineTechnology_V2.0.0\\Graduation-Project\\web\\fileresources\\文本文件下载测试.txt";
File file=new File(filePath);
if (file!=null&&file.exists()){
FileInputStream fis=new FileInputStream(file);
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(isr);
/**
* ②设置响应头的相关属性
*/
response.setContentType("application/octet-stream;charset=utf-8");
//这里文件名切记要用URLEncode重新编码一下,否则下载下来的文件名不识别,导致文件无法打开
response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(file.getName(),"UTF-8"));
/**
* ③获取HttpServletResponse对象的字节输出流
* 因为下载的是文本类型文件,所以可以直接使用字符流输出
*/
OutputStream fos=response.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(fos);
BufferedWriter bw=new BufferedWriter(osw);
String line=br.readLine();
while (line!=null){
bw.write(line);
line=br.readLine();
}
bw.flush();
//记得关闭I/O流
bw.close();
osw.close();
fos.close();
br.close();
isr.close();
fis.close();
}
}
1)前端代码
<button id="filedownload5" class="layui-btn">代码临时处理的bean实体数据或数据库数据下载</button>
<script>
/*代码临时处理的bean实体数据或数据库数据下载*/
$('#filedownload5').click(function () {
window.location.href="<%=request.getContextPath()%>/test/downlodafile_4";
});
});
</script>
2)后端代码
/**
* 下载代码临时处理的bean实体数据或数据库数据
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/downlodafile_4")
public void downloadFileController4(HttpServletRequest request,HttpServletResponse response) throws IOException {
//设置自定义数据
FormDataReceive formDataReceive=new FormDataReceive();
formDataReceive.setUsername("dalei");
formDataReceive.setPassword("123456789");
//设置响应头相关格式
response.setContentType("application/ectet-stream;charset=utf-8");
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode("自定义数据下载.txt","UTF-8"));
//获取输出流
OutputStream ops=response.getOutputStream();
//自定义内容写入输出流
ops.write(formDataReceive.toString().getBytes());
//关闭输出流
ops.close();
}
附:FormDataReceive 封装类代码
import org.springframework.web.multipart.MultipartFile;
/**
* 完整表单内容封装实体类
* @author dalei
*/
public class FormDataReceive {
/**
* 这里几个属性的名称要与前端表单的每一个name属性保持一致,
* 负责会导致前端表单数据无法封装进来,造成参数丢失,或者可能会
* 报404错误
* @author dalei
*/
private String username;
private String password;
private MultipartFile file;
public FormDataReceive() {
}
public FormDataReceive(String username, String password, MultipartFile file) {
this.username = username;
this.password = password;
this.file = file;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
@Override
public String toString() {
return "FormDataReceive{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", file=" + file +
'}';
}
}
1)前端代码(记得加载JQuery哦)
<button id="filedownload3" class="layui-btn">文本类型文件下载 - ResponseEntity</button>
<script>
$(document).ready(function () {
/*文本类型文件下载 - ResponseEntity按钮监听器*/
$('#filedownload3').click(function () {
window.location.href="<%=request.getContextPath()%>/test/downloadfile_3";
});
</script>
2)后端代码
/**
* 使用ResponseEntity下载文件
* 流程:①获取文件输入流或生成临时文件
* ②设置HttpHeaders对象的ContentType值为MediaType.APPLICATION_OCTET_STREAM
* ③返回一个ResponseEntity对象,HttpStatus使用OK状态,负责会导致个别浏览器无法下载资源。
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/downloadfile_3")
public ResponseEntity<byte[]> downlodaFileController3(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取HttpHeaders对象
HttpHeaders httpHeaders=new HttpHeaders();
String filePath="D:\\Java\\idea\\Projects\\MedicineTechnology_V2.0.0\\Graduation-Project\\web\\fileresources\\文本文件下载测试.txt";
//获取文件
File file=new File(filePath);
if (file!=null&&file.exists()){
//设置Content-Type和Content-Disposition的值
httpHeaders.setContentType( MediaType.APPLICATION_OCTET_STREAM);
httpHeaders.setContentDispositionFormData("attachment",URLEncoder.encode(file.getName(),"UTF-8"));
//返回ResponseEntity对象
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK);
} else {
return null;
}
}