第十章 文件上傳與下載

target

了解使用 JavaWeb方式的文件上傳
掌握文件上傳的表單設(shè)置
掌握使用 SpringMVC 進(jìn)行文件上傳
理解使用SpringMVC進(jìn)行文件下載

文件上傳你是任何一個項目都必不缺少的,抖音里面頭像的上傳,QQ里面視頻的上傳嚷量,拼多多里面商品詳情介紹等,文件上傳隨處可見蜓竹。下面介紹JavaWeb方式的原始上傳和SpringMVC方式的上傳。

1. 文件上傳-JavaWeb方式

對于文件上傳储藐,瀏覽器在上傳的過程中是將文件以流的形式提交到服務(wù)器端的俱济,如果直接使用Servlet獲取上傳文件的輸入流然后再解析里面的請求參數(shù)是比較麻煩,所以一般選擇采用apache的開源工具common-fileupload這個文件上傳組件钙勃。common-fileupload是依賴于common-io這個包的蛛碌,所以還需要下載這個包。

新建動態(tài)Web項目:FileUpload-1

在WebContent目錄下新建上傳表單:upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上傳</title>
</head>
<body>

    <form method="post" action="${pageContext.servletContext.contextPath }/upload"
        enctype="multipart/form-data">
        上傳用戶:<input type="text" name="username"><br> 
        上傳文件:<input type="file" name="myfile" multiple="multiple"><br> 
        <input type="submit" value="上傳">
    </form>

</body>
</html>

新建 UploadServlet類處理文件上傳:

package com.lee.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final int FILE_SIZE = 10;// 單個文件最大值
    private static final String EXT_NAME = "gif,pdf,dmg,jpg,jpeg,png,bmp,swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb,doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2";// 定義允許上傳的文件名
    private static final String savePath = "WEB-INF/upload";
    private static final String temPath = "WEB-INF/tem";
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 得到上傳文件的保存目錄辖源,將上傳文件存放在WEB-INF目錄下蔚携,不允許外界直接訪問,保證上傳文件的安全
        String saveRealPath = this.getServletContext().getRealPath(savePath);
        createDir(saveRealPath);// 創(chuàng)建WEB-INF/upload目錄

        // 上傳時生成臨時文件保存目錄
        String saveTemPath = this.getServletContext().getRealPath(temPath);
        File temDir = createDir(saveTemPath);// 創(chuàng)建WEB-INF/tem文件夾

        String message = "";// 消息提醒

        String fileName = "";// 上傳的文件名

        // 使用Apache文件上傳組件處理文件上傳步驟
        try {
            /**
             * 1.創(chuàng)建DiskFileItemFactory對象
             */
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 設(shè)置工廠的緩沖區(qū)大小克饶,當(dāng)上傳的文件大小超過緩沖區(qū)大小時酝蜒,就會生成一個臨時文件存放在指定的臨時目錄之中
            // 設(shè)置緩沖區(qū)大小為100kb,如果不設(shè)置矾湃,默認(rèn)緩沖區(qū)大小是10kb
            factory.setSizeThreshold(1024 * 100);
            // 設(shè)置上傳時生成的臨時文件的保存目錄
            factory.setRepository(temDir);

            /**
             * 2.創(chuàng)建文件上傳解析器
             */
            ServletFileUpload upload = new ServletFileUpload(factory);

            // 解決上傳文件名的中文亂碼問題
            upload.setHeaderEncoding("UTF-8");

            // 判斷提交上來的數(shù)據(jù)是否是文件表單
            if (!ServletFileUpload.isMultipartContent(req)) {
                // 終止文件上傳處理
                return;
            }
            // 獲取文件表單中的所有文件
            List<FileItem> items = upload.parseRequest(req);
            for (FileItem item : items) {
                if (item.isFormField()) {// formfield中封裝的是普通的輸入項數(shù)據(jù)
                    String name = item.getFieldName();
                    // 解決輸入項數(shù)據(jù)中文亂碼問題
                    String value = item.getString("UTF-8");
                    // value = new String(value.getBytes("iso8859-1"),"UTF-8");
                    System.out.println(name + "=" + value);
                } else {// formfield中封裝的是上傳文件
                    fileName = item.getName();// 得到文件上傳名
                    System.out.println("文件名:" + fileName);
                    if (fileName == null || fileName.trim().length() == 0) {
                        continue;
                    }

                    /**
                     * 注意:不同的瀏覽器提交的文件名是不一樣的亡脑,有些瀏覽器提交上來的文件名是帶有路徑的 如: C:\Users\H__D\Desktop\1.txt
                     * 而有些則是: 1.txt
                     */

                    // 處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分
                    fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
                    // 得到上傳文件的擴(kuò)展名
                    String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

                    // 通過擴(kuò)展名判斷上傳文件是否合法
                    System.out.println("上傳文件的擴(kuò)展名是:" + fileExt);
                    if (!EXT_NAME.contains(fileExt)) {
                        System.out.println("不允許上傳" + fileExt + "格式的文件邀跃!");
                        message += "文件:" + fileName + ",上傳文件擴(kuò)展名不允許是" + fileExt + "<br>";
                        continue;
                    }

                    // 檢查文件大小
                    if (item.getSize() == 0 || item.getSize() > FILE_SIZE * 1024 * 1024) {
                        message += "文件大小為:" + item.getSize() / 1024 / 1024 + "M,規(guī)定大小不超過" + FILE_SIZE + "M<br>";
                        continue;
                    }

                    // 得到存儲的文件名
                    String saveFileName = makeFileName(fileName);
                    // 獲取item中上傳文件的輸入流
                    InputStream is = item.getInputStream();
                    // 創(chuàng)建文件輸出流
                    FileOutputStream out = new FileOutputStream(saveRealPath + File.separator + saveFileName);
                    // 創(chuàng)建一個緩沖區(qū)
                    byte[] b = new byte[1024];
                    int len = 0;
                    while ((len = is.read(b)) > 0) {
                        out.write(b, 0, len);
                    }
                    // 關(guān)閉流
                    out.close();
                    is.close();
                    // 刪除臨時文件
                    item.delete();
                    message += "文件:" + fileName + "上傳成功!<br>";
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            message += fileName + "上傳失斆棺伞!";
        }

        req.setAttribute("message", message);
        req.getRequestDispatcher("WEB-INF/views/message.jsp").forward(req, resp);
    }

    /**
     * 為防止文件覆蓋現(xiàn)象坞嘀,為上傳文件產(chǎn)生一個唯一的文件名
     * 
     * @param fileName
     * @return
     */
    private String makeFileName(String fileName) {
        return UUID.randomUUID().toString().replaceAll("-", "") + "_" + fileName;
    }

    /**
     * 創(chuàng)建目錄
     * 
     * @param path
     */
    private File createDir(String path) {
        File pathDir = new File(path);
        if (!pathDir.exists()) {
            // 創(chuàng)建WEB-INF/upload目錄
            pathDir.mkdirs();
        }
        return pathDir;
    }
}

在WEB-INF/views目錄下新建消息頁面:message.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>提示</title>
</head>
<body>
${message }
</body>
</html>

訪問地址:http://localhost:8080/File/upload.jsp

此種方式可以上傳一個或多個文件躯护。

文件上傳到服務(wù)器的位置"upload",該位置是指:workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps

幾個細(xì)節(jié):

①為保證服務(wù)器安全惊来,上傳文件應(yīng)該放在外界無法直接訪問的目錄下丽涩,比如放于WEB-INF目錄下。
②為防止文件覆蓋的現(xiàn)象發(fā)生,要為上傳文件產(chǎn)生一個唯一的文件名矢渊。
③要限制上傳文件的最大值继准。
④要限制上傳文件的類型,在收到上傳文件名時矮男,判斷后綴名是否合法移必。
⑤表單的enctype="multipart/form-data" method="post"

2. 文件上傳-SpringMVC方式

Spring MVC 框架的文件上傳是基于 commons-fileupload 組件的文件上傳,只不過 Spring MVC 框架在原有文件上傳組件上做了進(jìn)一步封裝毡鉴,簡化了文件上傳的代碼實現(xiàn)崔泵,取消了不同上傳組件上的編程差異。

commons-fileupload 組件依賴于 Apache 的另外一個項目(commons-io)猪瞬,因此需要導(dǎo)入commons-io的jar包憎瘸。

Commons 是 Apache 開放源代碼組織中的一個 Java子項目,該項目包括文件上傳陈瘦、命令行處理幌甘、數(shù)據(jù)庫連接池、XML 配置文件處理等模塊痊项。fileupload 就是其中用來處理基于表單的文件上傳的子項目锅风,commons-fileupload 組件性能優(yōu)良,并支持任意大小文件的上傳鞍泉。

2.1 表單屬性

由于多數(shù)文件上傳都是通過表單形式提交給后臺服務(wù)器的皱埠,因此,要實現(xiàn)文件上傳功能咖驮,就需要提供一個文件上傳的表單漱逸。同時該表單必須滿足以下3個條件:

  • form表單的method屬性設(shè)置為post;
  • form表單的enctype屬性設(shè)置為multipart/form-data游沿;
  • 提供<input type="file" name="fileName" />的文件上傳輸入框饰抒。

表單的 enctype 屬性指定的是表單數(shù)據(jù)的編碼方式,該屬性有以下 3 個值:

  • application/x-www-form-urlencoded:這是默認(rèn)的編碼方式诀黍,它只處理表單域里的 value 屬性值袋坑。
  • multipart/form-data:該編碼方式以二進(jìn)制流的方式來處理表單數(shù)據(jù),并將文件域指定文件的內(nèi)容封裝到請求參數(shù)里眯勾。
  • text/plain:該編碼方式只有當(dāng)表單的 action 屬性為“mailto:”URL 的形式時才使用枣宫,主要適用于直接通過表單發(fā)送郵件的方式。

在HTML5之前吃环,如果想要上傳多個文件也颤,必須使用多個<input>元素。但是在HTML5中郁轻,在<input>元素配置multiple屬性即可進(jìn)行多個文件的上傳:

<input type="file" name="fileName" multiple />

2.3 MultipartFile接口

在 Spring MVC 框架中上傳文件時將文件相關(guān)信息及操作封裝到 MultipartFile 對象中翅娶,因此開發(fā)者只需要使用 MultipartFile 類型聲明模型類的一個屬性即可對被上傳文件進(jìn)行操作文留。該接口具有如下方法:

名稱 作用
byte[] getBytes() 以字節(jié)數(shù)組的形式返回文件的內(nèi)容
String getContentType() 返回文件的內(nèi)容類型
InputStream getInputStream() 返回一個InputStream,從中讀取文件的內(nèi)容
String getName() 返回請求參數(shù)的名稱
String getOriginalFillename() 返回客戶端提交的原始文件名稱
long getSize() 返回文件的大小竭沫,單位為字節(jié)
boolean isEmpty() 判斷被上傳文件是否為空
void transferTo(File destination) 將上傳文件保存到目標(biāo)目錄下

在上傳文件時需要在配置文件中使用 Spring 的 org.springframework.web.multipart.commons.CommonsMultipartResolver 類配置 MultipartResolver 用于文件上傳燥翅。

2.3 springmvc配置文件

當(dāng)客戶端form表單的enctype屬性為multipart/form-data時,瀏覽器就會采用二進(jìn)制流的方式來處理表單數(shù)據(jù)蜕提,而對于文件上傳的處理則涉及在服務(wù)器端解析原始的HTTP請求森书。Spring MVC為文件上傳提供了直接的支持,這種支持是用即插即用的MultipartResolver實現(xiàn)的谎势。

在Sring MVC中使用MultipartResolver也非常簡單凛膏,只需要在Spring MVC配置文件springmvc.xml中定義MultipartResolver接口實現(xiàn)類即可,示例如下:

<!-- 配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <!-- 設(shè)置最大允許上傳的文件的大小脏榆,單位是B -->
  <property name="maxUploadSize" value="50000000"></property>
  <!-- 設(shè)置最大允許上傳的單個文件的大小译柏,單位是B -->
  <property name="maxUploadSizePerFile" value="10000000"></property>
  <property name="defaultEncoding" value="UTF-8"></property>
</bean>

注意:

由于MultipartResolver實現(xiàn)類CommonsMultipartResolver內(nèi)部是引用multipartResolver字符串獲取該實現(xiàn)類對象并完成文件上傳操作的,所以在配置CommonsMultipartResolver時必須指定該bean的id為multipartResolver姐霍。

由于MultipartResolver實現(xiàn)類CommonsMultipartResolver是Spring MVC內(nèi)部通過Apache Commons FileUpload技術(shù)實現(xiàn)的鄙麦。因此Spirng MVC的文件上傳還需要依賴Apache Commons FileUpload的組件,即需要導(dǎo)入支持文件上傳和下載的JAR包镊折,具體如下:

  • commons-fileupload.jar
  • commons-io.jar

2.4 代碼實現(xiàn)

新建動態(tài)Web項目:FileUpload-2

① 引入 jar

將一下jar 復(fù)制到lib文件夾下:

commons-fileupload-1.4.jar
commons-io-2.6.jar
commons-logging-1.2.jar
spring-aop-4.3.9.RELEASE.jar
spring-beans-4.3.9.RELEASE.jar
spring-context-4.3.9.RELEASE.jar
spring-core-4.3.9.RELEASE.jar
spring-expression-4.3.9.RELEASE.jar
spring-web-4.3.9.RELEASE.jar
spring-webmvc-4.3.9.RELEASE.jar

② 創(chuàng)建 web.xml 文件

在 WEB-INF 目錄下創(chuàng)建 web.xml 文件胯府。為防止中文亂碼,需要在 web.xml 文件中添加字符編碼過濾器恨胚。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>FileUpload-2</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  
  <!-- The front controller of this Spring Web application, responsible for 
        handling all application requests -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

③ 創(chuàng)建 springmvc配置文件

在 src 下配置 springmvc.xml 骂因,配置包掃描、視圖解析器以及文件上傳相關(guān)設(shè)置赃泡。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!-- 使用掃描機(jī)制掃描控制器類 -->
    <context:component-scan
        base-package="com.lee.controller" />

    <!-- 配置視圖解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 配置文件解析器 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 設(shè)置最大允許上傳的文件的大小寒波,單位是B -->
        <property name="maxUploadSize" value="1000000000"></property>
        <!-- 設(shè)置最大允許上傳的單個文件的大小,單位是B -->
        <property name="maxUploadSizePerFile" value="100000000"></property>
        <property name="defaultEncoding" value="UTF-8"></property>
    </bean>
</beans>

開發(fā)經(jīng)驗:一定要加上id="multipartResolver"升熊,否則會報錯400俄烁。
④ 文件上傳頁面

在 WebContent 目錄下創(chuàng)建 JSP頁面 upload.jsp,在該頁面中使用表單上傳文件(可以一個或多個文件呢)级野,具體代碼如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SpringMVC方式上傳</title>
</head>
<body>

    <form action="${pageContext.servletContext.contextPath }/upload" method="post" enctype="multipart/form-data">
        上傳文件:<input type="file" name="files" multiple />
         <input type="submit" value="上傳">
    </form>
</body>
</html>

⑤ 創(chuàng)建控制器類

package com.lee.controller;

@Controller
public class UploadController {
    private static final String EXT_NAME = "gif,pdf,dmg,jpg,jpeg,png,bmp,swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb,doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2";// 定義允許上傳的文件名
    private static final String SAVE_PATH = "WEB-INF/upload";

    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("files") List<MultipartFile> files, HttpServletRequest req,
            Map<String, Object> map) {

        String message = "";
        // 對上傳的文件進(jìn)行解析
        if (files != null && files.size() > 0) {
            for (MultipartFile file : files) {
                // 獲取文件的原始名字
                String filename = file.getOriginalFilename();
                // 設(shè)置文件保存目錄
                String saveRealPath = req.getServletContext().getRealPath(SAVE_PATH);
                File saveDir = new File(saveRealPath);
                if (!saveDir.exists()) {
                    saveDir.mkdirs();
                }

                // 判斷文件格式是否符合
                String extFileName = filename.substring(filename.lastIndexOf(".") + 1);
                if (!EXT_NAME.contains(extFileName)) {
                    message += filename + "格式不符合页屠!<br>";
                    map.put("message", message);
                    continue;
                }
                // 設(shè)置保存文件的名字:UUID或者時間進(jìn)行重新命名
                String newFileName = UUID.randomUUID().toString().replaceAll("-", "") + "_" + filename;

                // 使用multipartFile接口上傳
                try {
                    File saveFile = new File(saveRealPath + File.separator + newFileName);
                    file.transferTo(saveFile);
                    message += filename + "上傳成功!<br>";
                } catch (Exception e) {
                    e.printStackTrace();
                    message += filename + "上傳失敱腿帷辰企!<br>";
                }
                map.put("message", message);
            }
        }
        return "success";
    }
}

⑥ 創(chuàng)建成功顯示頁面

在 WEB-INF/views 目錄下創(chuàng)建頁面 success.jsp。具體代碼如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>提示</title>
  </head>
  <body>
    ${message }
  </body>
</html>

部署項目况鸣,進(jìn)行測試牢贸,可以選擇一個或多個文件測試。

3. 文件下載

3.1 實現(xiàn)方法

實現(xiàn)文件下載有以下兩種方法:

  • 通過超鏈接實現(xiàn)下載镐捧。
  • 利用程序編碼實現(xiàn)下載潜索。

通過超鏈接實現(xiàn)下載固然簡單臭增,但暴露了下載文件的真實位置,并且只能下載存放在 Web 應(yīng)用程序所在的目錄下的文件帮辟。

利用程序編碼實現(xiàn)下載可以增加安全訪問控制,還可以從任意位置提供下載的數(shù)據(jù)玩焰,可以將文件存放到 Web 應(yīng)用程序以外的目錄中由驹,也可以將文件保存到數(shù)據(jù)庫中。

利用程序?qū)崿F(xiàn)下載需要設(shè)置兩個報頭:

① Web 服務(wù)器需要告訴瀏覽器其所輸出內(nèi)容的類型不是普通文本文件或 HTML 文件昔园,而是一個要保存到本地的下載文件蔓榄,這需要設(shè)置 Content-Type 的值為 application/x-msdownload。

② Web 服務(wù)器希望瀏覽器不直接處理相應(yīng)的實體內(nèi)容默刚,而是由用戶選擇將相應(yīng)的實體內(nèi)容保存到一個文件中甥郑,這需要設(shè)置 Content-Disposition 報頭。

該報頭指定了接收程序處理數(shù)據(jù)內(nèi)容的方式荤西,在 HTTP 應(yīng)用中只有 attachment 是標(biāo)準(zhǔn)方式澜搅,attachment 表示要求用戶干預(yù)。在 attachment 后面還可以指定 filename 參數(shù)邪锌,該參數(shù)是服務(wù)器建議瀏覽器將實體內(nèi)容保存到文件中的文件名稱勉躺。

設(shè)置報頭的示例如下:

response.setHeader("Content-Type", "application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename="+filename);

3.2 代碼實現(xiàn)

下面繼續(xù)通過 FileUpload-2 應(yīng)用講述利用程序?qū)崿F(xiàn)下載的過程,要求從(workspace.metadata.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\WEB-INF\uploadf)中下載文件觅丰,具體開發(fā)步驟如下:

① 編寫控制器類

首先編寫控制器類 FileDownController饵溅,在該類中有 3 個方法棋凳,即 show咐扭、down 和 toUTF8String。其中路呜,show 方法獲取被下載的文件名稱冠句;down 方法執(zhí)行下載功能轻掩;toUTF8String 方法是下載保存時中文文件名的字符編碼轉(zhuǎn)換方法。

package com.lee.controller;

@Controller
public class FileDownController {

     /**
     * 顯示要下載的文件
     */
    @RequestMapping("showDownFiles")
    public String show(HttpServletRequest request, Model model) {
        // 從 workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\下載
        String realpath = request.getServletContext()
                .getRealPath("WEB-INF/upload");
        File dir = new File(realpath);
        File files[] = dir.listFiles();
        // 獲取該目錄下的所有文件名
        ArrayList<String> fileName = new ArrayList<String>();
        for (int i = 0; i < files.length; i++) {
            fileName.add(files[i].getName());
        }
        model.addAttribute("files", fileName);
        return "showDownFiles";
    }
    /**
     * 執(zhí)行下載
     */
    @RequestMapping("down")
    public String down(@RequestParam String filename,
            HttpServletRequest request, HttpServletResponse response) {
        String aFilePath = null; // 要下載的文件路徑
        FileInputStream in = null; // 輸入流
        ServletOutputStream out = null; // 輸出流
        try {
            // 從workspace\.metadata\.plugins\org.eclipse.wst.server.core\
            // tmp0\wtpwebapps下載
            aFilePath = request.getServletContext().getRealPath("WEB-INF/upload");
            // 設(shè)置下載文件使用的報頭
            response.setHeader("Content-Type", "application/x-msdownload");
            response.setHeader("Content-Disposition", "attachment; filename="
                    + toUTF8String(filename));
            // 讀入文件
            in = new FileInputStream(aFilePath + File.separator+ filename);
            // 得到響應(yīng)對象的輸出流懦底,用于向客戶端輸出二進(jìn)制數(shù)據(jù)
            out = response.getOutputStream();
            out.flush();
            int aRead = 0;
            byte b[] = new byte[1024];
            while ((aRead = in.read(b)) != -1 & in != null) {
                out.write(b, 0, aRead);
            }
            out.flush();
            in.close();
            out.close();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 下載保存時中文文件名的字符編碼轉(zhuǎn)換方法
     */
    public String toUTF8String(String str) {
        StringBuffer sb = new StringBuffer();
        int len = str.length();
        for (int i = 0; i < len; i++) {
            // 取出字符中的每個字符
            char c = str.charAt(i);
            // Unicode碼值為0~255時放典,不做處理
            if (c >= 0 && c <= 255) {
                sb.append(c);
            } else { // 轉(zhuǎn)換 UTF-8 編碼
                byte b[];
                try {
                    b = Character.toString(c).getBytes("UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    b = null;
                }
                // 轉(zhuǎn)換為%HH的字符串形式
                for (int j = 0; j < b.length; j++) {
                    int k = b[j];
                    if (k < 0) {
                        k &= 255;
                    }
                    sb.append("%" + Integer.toHexString(k).toUpperCase());
                }
            }
        }
        return sb.toString();
    }
}

② 創(chuàng)建文件列表頁面

下載文件示例需要一個顯示被下載文件的 JSP 頁面 showDownFiles.jsp,在WEB-INF/views目錄下編寫基茵,代碼如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
</head>
<body>
  <table>
    <tr>
      <td>被下載的文件名</td>
    </tr>
    <!--遍歷 model中的 files-->
    <c:forEach items="${files}" var="filename">
      <tr>
        <td>
          <a href="${pageContext.request.contextPath }/down?filename=${filename}">${filename}</a>
        </td>
      </tr>
    </c:forEach>
  </table>
</body>
</html>

③ 測試下載功能

將應(yīng)用部署到到 Tomcat 服務(wù)器并啟動 Tomcat 服務(wù)器奋构,然后通過地址"http://localhost:8080/FileUpload-2/showDownFiles"測試下載示例。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拱层,一起剝皮案震驚了整個濱河市弥臼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌根灯,老刑警劉巖径缅,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掺栅,死亡現(xiàn)場離奇詭異,居然都是意外死亡纳猪,警方通過查閱死者的電腦和手機(jī)氧卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氏堤,“玉大人沙绝,你說我怎么就攤上這事∈笮猓” “怎么了闪檬?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長购笆。 經(jīng)常有香客問我粗悯,道長,這世上最難降的妖魔是什么同欠? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任样傍,我火速辦了婚禮,結(jié)果婚禮上铺遂,老公的妹妹穿的比我還像新娘铭乾。我一直安慰自己,他們只是感情好娃循,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布炕檩。 她就那樣靜靜地躺著,像睡著了一般捌斧。 火紅的嫁衣襯著肌膚如雪笛质。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天捞蚂,我揣著相機(jī)與錄音妇押,去河邊找鬼。 笑死姓迅,一個胖子當(dāng)著我的面吹牛敲霍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丁存,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肩杈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了解寝?” 一聲冷哼從身側(cè)響起扩然,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聋伦,沒想到半個月后夫偶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體界睁,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年兵拢,在試婚紗的時候發(fā)現(xiàn)自己被綠了翻斟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡说铃,死狀恐怖访惜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情截汪,我是刑警寧澤疾牲,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布植捎,位于F島的核電站衙解,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏焰枢。R本人自食惡果不足惜蚓峦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望济锄。 院中可真熱鬧暑椰,春花似錦、人聲如沸荐绝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽低滩。三九已至召夹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恕沫,已是汗流浹背监憎。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留婶溯,地道東北人鲸阔。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像迄委,于是被迫代替她去往敵國和親褐筛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容