文件下载案例
# 75.文件下载案例
我们来做一个下载文件的功能,加深我们对 Servlet 的使用。
# 需求
- 页面显示超链接
- 点击超链接后弹出下载提示框
- 完成图片文件下载
# 下载页面
新建一个 download.html
在 web 目录下新建 img 目录,并放两个图片 1.jpg 2.jpg
如果超链接直接指向图片资源的路径,那么由于该图片能被浏览器显示,浏览器会直接展示,而不是弹出下载提示框:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件下载案例</title>
</head>
<body>
<a href="/hello/img/1.jpg">图片1</a>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
访问 http://localhost: 8080/hello/download.html,点击超链接,浏览器会直接展示图片:
而如果超链接指向的文件,不能被浏览器直接显示,才会提示下载框,例如我们放一个视频文件 1.mp4
<a href="/hello/img/1.jpg">图片1</a> <br>
<a href="/hello/img/1.mp4">视频1</a> <br>
1
2
2
解决方法:我们设置响应头,告诉浏览器资源的打开方式为下载
content-disposition:attachment;filename=xxx
1
为此,我们需要请求的是 Servlet 的路径,并且传参文件名,告诉 Servlet 我们要的是什么文件
<a href="/hello/downLoadServlet?fileName=1.jpg">图片1</a> <br>
<a href="/hello/downLoadServlet?fileName=1.mp4">视频1</a> <br>
1
2
2
# 下载 Servlet
接下来就是实现 Servlet,步骤如下
- 获取文件名称
- 使用字节输入流加载文件进内存,涉及到真实路径
- 指定 response 的响应头:
content-disposition: attachment;filename=xxx
- 将数据写出到 response 输出流
完整代码:
package com.peterjxl.download;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/downLoadServlet")
public class DownLoadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求参数
String fileName = req.getParameter("fileName");
// 2. 使用字节输入流加载文件进内存
// 2.1 找到文件真实路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/" + fileName);
// 2.2 使用字节流关联
FileInputStream fis = new FileInputStream(realPath);
// 3. 设置响应头
// 3.1 设置响应头:content-type
String mimeType = servletContext.getMimeType(fileName);
resp.setHeader("content-type", mimeType);
// 3.2 设置响应头打开方式:content-disposition
resp.setHeader("content-disposition", "attachment;filename=" + fileName);
// 4. 将输入流的数据,输出到response的输出流
ServletOutputStream sos = resp.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0
while ( -1 != (len = fis.read(buff))){
sos.write(buff, 0, len);
}
fis.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
重启 Tomcat,访问 http://localhost: 8080/hello/download.html,分别测试,可以看到有下载提示框
# 如果文件名包含中文名
如果文件名带有中文,还能正常下载吗?我们来测试下。
在下载页面增加一个下载链接:
<hr>
<a href="/hello/downLoadServlet?fileName=这是什么元气美少女!.jpg">这是什么元气美少女!.jpg</a> <br>
1
2
2
然后准备一个图片文件到 img 目录下,并重命名为“这是什么元气美少女!.jpg”
重启 Tomcat,访问并尝试下载,很遗憾,报错了:
java.lang.IllegalArgumentException: 代码点[20,061]处的Unicode字符[这]无法编码,因为它超出了允许的0到255范围。
1
这是因为中文编码的问题。解决思路:
- 获取客户端使用的浏览器版本信息
- 根据不同的版本信息,使用 URLEncoder 来设置 filename 的编码方式
我们首先定义一个下载工具类:
package com.peterjxl.util;
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
然后修改 DownloadServlet 的代码:
String agent = req.getHeader("user-agent");
fileName = DownLoadUtils.getFileName(agent, fileName);
resp.setHeader("content-disposition", "attachment;filename=" + fileName);
1
2
3
2
3
重启 Tomcat 再次访问,就可以下载了
# 总结
- 如果超链接指向的资源能够被浏览器解析,则会直接在浏览器中展示;如果不能解析,则弹出下载提示框
- 弹出下载提示框:响应头设置资源的打开方式:content-disposition: attachment; filename = xxx
- 注意中文问题
上次更新: 2024/10/1 21:14:36