通過Servlet實現文件的下載


  當我們實現一個文件下載功能時,大多數人是通過Strust等框架實現的。Strust框架把底層的文件下載細節隱藏了起來,使我們不得其要領。下面我通過一個程序示例來再現通過Servlet下載文件的細節和原理。

程序源碼

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

    <!--可以配置一些Listener 或 Filter 來觀察一些對象的生命周期 -->
    
    <servlet>
        <servlet-name>download_servlet</servlet-name>
        <servlet-class>edu.shao.webapp.sample.DownloadServlet</servlet-class>
        <init-param>
            <param-name>fileRoot</param-name>
            <param-value>d:/</param-value>
        </init-param>
        <init-param>
            <param-name>contentType</param-name>
            <param-value>application/octet-stream</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>download_servlet</servlet-name>
        <url-pattern>/download</url-pattern>
    </servlet-mapping>
</web-app>

DownloadServlet.java

 1 package edu.shao.webapp.sample;
 2 
 3 import java.io.BufferedInputStream;
 4 import java.io.File;
 5 import java.io.FileInputStream;
 6 import java.io.IOException;
 7 import java.net.URLEncoder;
 8 
 9 import javax.servlet.ServletConfig;
10 import javax.servlet.ServletException;
11 import javax.servlet.ServletOutputStream;
12 import javax.servlet.http.HttpServlet;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 
16 import org.apache.logging.log4j.LogManager;
17 import org.apache.logging.log4j.Logger;
18 
19 public class DownloadServlet extends HttpServlet{
20     private static final long serialVersionUID = 1L;
21     public static Logger logger=LogManager.getLogger(DownloadServlet.class);
22     
23     private String contentType;
24     private String enc="UTF-8";
25     private String fileRoot;
26     
27     @Override
28     public void init(ServletConfig config) throws ServletException {
29         contentType = config.getInitParameter("contentType");
30         fileRoot = config.getInitParameter("fileRoot");
31     }
32 
33     @Override
34     public void doGet(HttpServletRequest req, HttpServletResponse resp)
35             throws ServletException, IOException {
36         logger.debug("Do Get Method.");
37         String fileName=req.getParameter("fileName");
38         String filePath=fileRoot+File.separator+fileName;
39         
40         File downloadFile=new File(filePath);
41         if (downloadFile.exists()) {
42             logger.info("File exist");
43             
44             resp.setContentType(contentType);
45             Long length=downloadFile.length();
46             resp.setContentLength(length.intValue());
47             fileName = URLEncoder.encode(downloadFile.getName(), enc);
48             resp.addHeader("Content-Disposition", "attachment; filename=" + fileName);
49             
50             ServletOutputStream servletOutputStream=resp.getOutputStream();
51             FileInputStream fileInputStream=new FileInputStream(downloadFile);
52             BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream);
53             int size=0;
54             byte[] b=new byte[4096];
55             while ((size=bufferedInputStream.read(b))!=-1) {
56                 logger.info("write to output stream..");
57                 servletOutputStream.write(b, 0, size);
58             }
59             servletOutputStream.flush();
60             servletOutputStream.close();
61             bufferedInputStream.close();
62         }else {
63             logger.info("File is not exist");
64         }
65     }
66 
67 }

解釋:

1、contentType用於定義用戶的瀏覽器如何顯示將要加載的數據,或者如何處理將要加載的數據。如網頁的contentType是text/html,jpeg圖片的contentType是image/jpeg。如果不知道文件類型,可以設置為二進制流文件 application/octet-stream

2、contentType和fileRoot(待下載文件的目錄)通過web.xml配置。

3、resp.setContentType()、resp.setContentLength()、resp.addHeader("Content-Disposition", "attachment; filename=" + fileName); 三個調用設置一些必要的響應報頭(reponse header)。

 

運行結果:

1、瀏覽器地址欄輸入:http://localhost:8080/test/download?fileName=test.mkv  回車(文件“d:/test.mkv”的大小為700M)

2、瀏覽器彈出下載提示窗口,但未點擊保存。控制台先打印幾條日志,然后停滯,說明已經向輸出流寫入了4kB左右(程序中byte數組的大小為4096B)的數據了。

3、選擇保存位置后,點擊保存。此時控制台開始瘋狂地輸出log。。。

4、最后下載完成,此次Request結束。

小結:

  文件下載的原理非常簡單,就是把數據從一個輸入流中讀出數據,再寫入一個輸出流。這里的輸入流是FileInputStream(為了提高速度,對其包裝了一個裝飾類BufferedInputStream,以提供緩沖功能),輸出流是ServletOutputStream。

  源碼中的定義,public abstract class ServletOutputStream extends OutputStream。說明ServletOutputStream繼承了OutputStream,它和FileOutputStream等輸出流是一樣的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM