一、功能性及非功能性需求:
文件批量下載,支持斷點續傳。支持批量下載1000個文件。
使用JS能夠實現批量下載,能夠提供接口從指定url中下載文件並保存在本地指定路徑中。
服務器不需要打包。
支持大文件斷點下載。比如下載10G的文件。
PC端全平台支持。Windows,macOS,Linux
全瀏覽器支持。ie6,ie7,ie8,ie9,ie10,ie11,edge,firefox,chrome,safari
JavaWeb 文件下載功能
文件下載的實質就是文件拷貝,將文件從服務器端拷貝到瀏覽器端,所以文件下載需要IO技術將服務器端的文件讀取到,然后寫到response緩沖區中,然后再下載到個人客戶端。
1. 文件名 - 接受前端發來的文件名
獲取到前端頁面發送過來的要下載的文件的名字
String filenameValue = req.getParameter("filename");
2. ServletContext域 - 獲取到ServletContext域對象
后面將調用此對象的一系列方法,用於獲取文件路徑、文件MimeType,並設置文件輸出類型
ServletContext servletContext = req.getServletContext(); //獲取到ServletContext域對象
3. 文件路徑 - 獲取指定文件在web項目中的路徑
通過獲取到ServletContext域對象的getRealPath()方法,讀取download目錄下文件的絕對路徑
注意:download目錄必須放在webContent目錄下面,否則可能會找不到,導致報異常,在讀取資源的時候,項目demo會直接去查找webContent下面的文件和文件夾
String realPath = servletContext.getRealPath("download/"+filenameValue); //獲取到要下載文件在web項目中的絕對路徑
4. 文件MimeType - 獲取文件的MimeType類型
通過獲取到的 ServletContext 域對象的 getMimeType() 方法,獲取到文件MimeType
MIME (Multipurpose Internet Mail Extensions) 是描述消息內容類型的因特網標准。
MIME 協議指示 MIME 用戶代理如何顯示附加的文件。
MIME 參考手冊:http://www.w3school.com.cn/media/media_mimeref.asp
告知瀏覽器文件的類型:response.setContentType(文件的MIME類型);
String mimeType = servletContext.getMimeType(filenameValue); //獲取到要下載文件的mimeType類型
5. 輸出類型 - 設置文件的輸出類型
根據之前獲取到的文件MimeType,然后通過 Response 域對象的 setContentType() 方法,設置文件的輸出類型
resp.setContentType(mimeType); //設置文件的輸出類型
6. 設置響應頭 - 確定文件是內嵌或彈出下載框
通過 Response 域對象的 setHeader("Content-Disposition","attachment;filename="+filename) 方法設置響應頭
Content-Disposition(內容處置/處理)
是 MIME 協議的擴展,Content-Disposition 可以控制用戶請求所得的內容存為一個文件時提供一個默認的文件名
inline 和 attachment:文件直接在瀏覽器上顯示或者在訪問時彈出文件下載對話框。
inline 表示:內嵌顯示,文本和圖片都可以解析,但對於文件或者視頻會自動去調用成attachment,因此可以直接使用inline
attachment:彈出下載框,因為attachment是讓文件以附件的形式打開,因此會調用下載,但此下載的功能並沒有提示
//設置輸出(下載)的文件的默認文件名為filenameValue的值,inline表示內嵌文本和圖片,文件和視頻會自動調用成attachment
resp.setHeader("Content-Disposition", "inline;filename="+filenameValue);
7. 執行輸出(下載) - IO流
7.1 通過 new,創建字節輸入流 FileInputStream,讀取文件
7.2 通過Response域,創建Servlet的輸出流,輸出文件
FileInputStream fileInputStream = new FileInputStream(realPath);
ServletOutputStream outputStream = resp.getOutputStream();
int b = 0;
byte[] by = new byte[1024 * 8];
while ((b = fileInputStream.read(by)) != -1) {
outputStream.write(by, 0, b);
}
outputStream.flush();
fileInputStream.close();
outputStream.close(); //關流,response獲得流會自動關閉,因此也可以不用手動關
功能實現代碼
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletConfig;
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;
public class Download extends HttpServlet {@Override public void init(ServletConfig config) throws ServletException {
/**重寫了Servlet的init(ServletConfig config)方法后一定要記得調用父類的init方法,
* 否則在service/doGet/doPost方法中使用getServletContext()方法獲取ServletContext對象時
* 就會出現java.lang.NullPointerException異常
* */
super.init(config);
}@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
doPost(req, resp);
}@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
/**1. 接受前端頁面發送過來的文件名字
* 獲取到前端頁面發送過來的要下載的文件的名字
* */
String filenameValue = req.getParameter("filename");
//---------------------
// filenameValue = URLEncoder.encode(filenameValue, "gbk");
/**2. 獲取到ServletContext域對象
* 后面將調用此對象的一系列方法,用於獲取文件路徑、文件MimeType、文件輸出類型
* */
ServletContext servletContext = req.getServletContext(); //獲取到ServletContext域對象
/**3. 獲取指定文件在web項目中的路徑
* 通過獲取到ServletContext域對象的getRealPath()方法,讀取download目錄下文件的絕對路徑
* download目錄必須放在webContent目錄下面,否則可能會找不到,導致報異常,
* 在讀取資源的時候,項目demo會直接去查找webContent下面的文件和文件夾
* */
String realPath = servletContext.getRealPath("download/" + filenameValue); //獲取到要下載文件在web項目中的絕對路徑
/**4. 獲取到文件MimeType
* 通過獲取到的ServletContext域對象的getMimeType()方法,獲取到文件MimeType
* MIME (Multipurpose Internet Mail Extensions) 是描述消息內容類型的因特網標准。
* MIME 協議指示 MIME 用戶代理如何顯示附加的文件。
* MIME 參考手冊:http://www.w3school.com.cn/media/media_mimeref.asp
* */
String mimeType = servletContext.getMimeType(filenameValue); //獲取到要下載文件的mimeType類型
/**5. 設置文件的輸出類型
* Response域對象的setContentType()方法,設置文件的輸出類型
* */
resp.setContentType(mimeType); //設置文件的輸出類型
/**6. 設置響應頭,確定文件是內嵌或彈出下載框
* 通過 Response 域對象的 setHeader("Content-Disposition","attachment;filename="+filename) 方法設置響應頭,
* Content-Disposition(內容處置/處理) :
* 是 MIME 協議的擴展,Content-Disposition 可以控制用戶請求所得的內容存為一個文件的時候提供一個默認的文件名,
* inline 和 attachment :
* 文件直接在瀏覽器上顯示或者在訪問時彈出文件下載對話框。
* inline 表示:內嵌顯示,文本和圖片都可以解析,但對於文件或者視頻會自動去調用成attachment,因此可以直接使用inline
* attachment:彈出下載框
* URLEncoder 對象,將在響應回去的頭,里面所代碼filename的編碼格式,轉換為與客戶端的一致的編碼格式
* URLEncoder.encode(filenameValue,"utf-8"); 將Response響應到瀏覽器客戶端為filenameValue的文件名,轉變為utf-8的編碼格式
*/
resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filenameValue, "utf-8")); //設置輸出(下載)的文件的默認文件名為filenameValue的值,inline表示內嵌文本和圖片
/**7. 輸出文件(下載文件)
* 7.1 通過 new,創建字節輸入流 FileInputStream,讀取文件
* 7.2 通過Response域,創建Servlet的輸出流,輸出文件
* */
FileInputStream fileInputStream = new FileInputStream(realPath);
ServletOutputStream outputStream = resp.getOutputStream();
int b = 0;
byte[] by = new byte[1024 * 8];
while ((b = fileInputStream.read(by)) != -1) {
outputStream.write(by, 0, b);
}
outputStream.flush();
fileInputStream.close();
outputStream.close(); //關流,response獲得流會自動關閉,因此也可以不用手動關
}
}
前端頁面 jsp 代碼 - /demo/WebContent/download.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<% String path=request.getContextPath(); String basePath=request.getScheme()+ "://"+request.getServerName()+ ":"+request.getServerPort()+path+ "/"; %>
<!DOCTYPE html>
<html>
<head>
<!-- <base> 標簽為頁面上的所有鏈接規定默認地址或默認目標。 -->
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>文件下載</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page"></head>
<body>
<a href="download?filename=JDBC連接主流數據庫.txt">點擊下載 文件</a>
<br/>
<a href="download?filename=0413102708.avi">點擊下載 視頻</a></body>
</html>web.xml - /demo/WebContent/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>demo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file></welcome-file-list>
<context-param>
<param-name>ServletContextName</param-name>
<param-value>ServletContextValue</param-value></context-param>
<servlet>
<servlet-name>Download</servlet-name>
<servlet-class>com.Download</servlet-class></servlet>
<servlet-mapping>
<servlet-name>Download</servlet-name>
<url-pattern>/download</url-pattern></servlet-mapping>
</web-app>
下載文件出現中文亂碼和不顯示文件名的情況
在有些情況下,如果下載中文文件,頁面在下載時會出現中文亂碼或不能顯示文件名的情況,原因是不同的瀏覽器默認對下載文件的編碼方式不同,比如ie是UTF-8編碼方式,而火狐瀏覽器是Base64編碼方式。
/**URLEncoder 對象,將在響應回去的頭,里面所代碼filename的編碼格式,轉換為與客戶端的一致的編碼格式
* URLEncoder.encode(filenameValue,"utf-8");
* 將Response響應到瀏覽器客戶端為filenameValue的文件名,轉變為utf-8的編碼格式
* */
resp.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(filenameValue,"utf-8"));
詳細配置信息可以參考這篇文章:http://blog.ncmem.com/wordpress/2019/08/28/java%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd/