文件下载案例实现
首先测试一下在html中什么样的文件设置超链接后点击可以直接下载
1.html测试
Html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="resource/sky.jpg">图片</a></li>
<li><a href="resource/Test.txt">txt文档</a></li>
<li><a href="resource/test.docx">doc文档</a></li>
</ul>
</body>
</html>
结果:
点击图片:
点击txt文档:
点击doc文档
所以对于图片、txt、html等文档可以直接在浏览器中渲染,不会出现下载另存为的选项,而对于浏览器渲染不了的doc文档,则会出现下载选项;
原因是:浏览器接收到响应报文以后,会尽可能的进行渲染处理,如果处理不了才会让用户下载保存。
要想不论什么类型的内容,都弹出另存为对话框,则需要改变浏览器的行为。依据是 HTTP协议。
HTTP协议指出,当响应报文包含以下头部信息时:
//Content-Dispositiont头字段说明类型。attachment附件
Content-Disposition:attachment;filename=文件名
Content-Type: application/octet-stream //核心另存为
2.测试ContentType
目录结构:
因为点击之后文件要进行响应头的设置,所以需要使用Servlet,
Html文件修改需要下载文件的链接地址:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="/download?filepath=resource/sky.jpg">图片</a></li>
<li><a href="/download?filepath=resource/Test.txt">txt文档</a></li>
<li><a href="resource/test.docx">doc文档</a></li>
</ul>
</body>
</html>
web.xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>download</servlet-name>
<servlet-class>com.example.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>download</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>
servlet代码:
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//拿到超链接传递过来的参数地址
String filepath = req.getParameter("filepath");
//设置响应头contentType
resp.setContentType("application/octet-stream");
//利用地址拿到对应的文件,因为不在同级目录,所以需要使用ServletContext来获取
ServletContext servletContext = this.getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("/" + filepath);
//利用字节流来进行输出另存为
ServletOutputStream outputStream = resp.getOutputStream();
byte [] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
}
}
点击测试结果:
这里有一个问题是点击后默认下载的的文件名称是download,为了修改默认文件名为原始文件名,需要设置
Content-Disposition:attachment;filename=文件名
//获取文件原始名称
filepath.lastIndexOf("/");
String name = filepath.substring(filepath.lastIndexOf("/")+1);
//设置响应头contentType
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Disposition","attachment;filename="+name);
完整代码:
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//拿到超链接传递过来的参数地址
String filepath = req.getParameter("filepath");
//获取文件原始名称
filepath.lastIndexOf("/");
String name = filepath.substring(filepath.lastIndexOf("/")+1);
//设置响应头contentType
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Disposition","attachment;filename="+name);
//利用地址拿到对应的文件,因为不在同级目录,所以需要使用ServletContext来获取
ServletContext servletContext = this.getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("/" + filepath);
//利用字节流来进行输出另存为
ServletOutputStream outputStream = resp.getOutputStream();
byte [] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
}
}
显示结果:
3.中文乱码
如果文件原始名为中文会出现乱码问题:
3.1不同的浏览器处理方式还不同。
- 非火狐的浏览器 文件名采用 URL编码
- 火狐浏览器 文件名采用 Base64编码
想要判断浏览器类型可以通过请求头中的User-Agent
3.2处理非火狐
name = URLEncoder.encode(name);
显示结果:
处理火狐
//如果是火狐浏览器,采用base64编码格式
//格式:=?utf-8?B?Base64字符串?=
//拿到转换成base64的字符串
String s = Base64.getEncoder().encodeToString(name.getBytes());
//拼接
name = "=?utf-8?B?"+s+"?=";
4.完整代码
html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li><a href="/download?filepath=resource/sky.jpg">图片</a></li>
<li><a href="/download?filepath=resource/Test.txt">txt文档</a></li>
<li><a href="/download?filepath=resource/资料.txt">资料</a></li>
<li><a href="resource/test.docx">doc文档</a></li>
</ul>
</body>
</html>
web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>download</servlet-name>
<servlet-class>com.example.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>download</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>
DownloadServlet 代码
package com.example.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.Base64;
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断浏览器的类型
String userAgent = req.getHeader("User-Agent");
boolean isFireFox = false;
if(userAgent!=null && userAgent.contains("FireFox")){
//采用火狐Base64编码格式
isFireFox = true;
}
//拿到超链接传递过来的参数地址
String filepath = req.getParameter("filepath");
//获取文件原始名称
filepath.lastIndexOf("/");
String name = filepath.substring(filepath.lastIndexOf("/")+1);
if (isFireFox){
//如果是火狐浏览器,采用base64编码格式
//格式:=?utf-8?B?Base64字符串?=
//拿到转换成base64的字符串
String s = Base64.getEncoder().encodeToString(name.getBytes());
//拼接
name = "=?utf-8?B?"+s+"?=";
}else {
name = URLEncoder.encode(name);
}
//设置响应头contentType
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Disposition","attachment;filename="+name);
//利用地址拿到对应的文件,因为不在同级目录,所以需要使用ServletContext来获取
ServletContext servletContext = this.getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("/" + filepath);
//利用字节流来进行输出另存为
ServletOutputStream outputStream = resp.getOutputStream();
byte [] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
}
}