Java 使用 Servlet 上傳下載文件


在實際開發中,上傳文件和下載文件是很常見的功能,如果文件名是中文的話,還容易會出現亂碼問題。

本篇博客采用 Servlet 作為接口演示 Java 上傳文件和下載文件的實現方案,同時解決獲取上傳和下載過程中所遇到的的中文文件名亂碼問題,並在本篇博客的最下面提供 demo 源代碼下載。


一、搭建工程

新建一個 maven 工程,並導入相關的 jar 包和 tomcat 插件,具體內容如下:

有關具體的 jar 包地址,可以在 https://mvnrepository.com 上進行查詢。

有關 tomcat 插件,可以到官網查詢,地址為:https://tomcat.apache.org/maven-plugin.html

目前 tomcat 官網的插件,最新版本也就是 tomcat7 的 2.2 版本。如果你想使用更高版本的 tomcat 的話,就下載獨立的 tomcat 即可。這里只是進行 demo 演示,就直接使用 tomcat 的插件了,能夠滿足 demo 運行的環境,使用起來也很方便。

<dependencies>
    <!--servlet支持-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!--文件上傳組件支持-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!--Apache提供的一些實用的工具類支持-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>
</dependencies>

<build>
    <finalName>fileUpDownTest</finalName>
    <plugins>
        <!--Tomcat官網最新版本的tomcat插件-->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <!--tomcat啟動的端口號-->
                <port>80</port>
                <!--網站啟動后訪問的虛擬地址-->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

由於要使用 servlet web 開發,需要導入 javax.servlet-api 的 jar 包。另外導入了 Apache 的 commons-fileupload 的 jar 包組件,使文件上傳變的很容易,同時導入了 commons-lang3 的 jar 包組件,主要是使用里面的一些字符串處理的實用方法,簡化代碼開發。最后打開右側的 Maven 窗口,刷新一下,這樣 Maven 會自動下載所需的 jar 包文件。

搭建好的項目工程整體目錄比較簡單,具體如下圖所示:

image

項目工程結構簡單介紹:

com.jobs.controller 包下面放了 2 個 Servlet 處理上傳和下載的請求
com.jobs.filter 包下面放了 1 個過濾器,主要設置請求和響應的字符編碼,統一解決亂碼問題

webapp 下的 upload 用來存放上傳的文件,里面提前放了一個中文名稱的 txt 文件,用於下載
webapp 下面放了一個 index.html 頁面文件,網站啟動時進行展示,提供文件上傳和下載操作


二、細節展示

這里先說 CharacterEncodingFilter 過濾器,統一設置請求和響應的字符編碼為 UTF-8 ,這樣可以方式亂碼。另外該過濾器配置的作用地址,本 demo 示例只處理 Servlet 請求,不處理其它請求,例如 css、js 等文件請求。具體內容如下:

package com.jobs.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//這里只監聽 /api/* 的請求,如果你想監聽所有請求,可以使用 /* 配置
//另外配置了過濾器的請求和響應的初始化參數,在下面的處理方法中可以獲取
@WebFilter(value = "/api/*",
        initParams = {
                @WebInitParam(name = "requestEncoding", value = "UTF-8"),
                @WebInitParam(name = "responseContentType", value = "text/html;charset=UTF-8")})
public class CharacterEncodingFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
                FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) resp;

            //獲取初始化參數
            String requestEncoding = filterConfig.getInitParameter("requestEncoding");
            String responseContentType = filterConfig.getInitParameter("responseContentType");

            //使用初始化參數,設置請求和響應的編碼
            request.setCharacterEncoding(requestEncoding);
            response.setContentType(responseContentType);

            chain.doFilter(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void destroy() {

    }
}

upLoadServlet 是專門用來處理上傳文件的請求,同時也接收 form 表單提交過來的字段,具體內容如下:

package com.jobs.controller;

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

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;

//配置訪問地址
@WebServlet("/api/upload")
public class upLoadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request,
                   HttpServletResponse resp) throws IOException {

        //判斷該操作是否支持文件上傳操作
        //要提交的form表單上必須要有屬性 enctype="multipart/form-data"
        if (ServletFileUpload.isMultipartContent(request)) {
            try {
                //將文件保存到 webapp 下的 upload 文件夾中
                String dir = this.getServletContext().getRealPath("upload");
                File f = new File(dir);
                if (!f.exists()) {
                    f.mkdir();
                }

                //采用 apache 的上傳組件,只需要編寫很少的代碼,即可完成文件上傳功能
                DiskFileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload fileUpload = new ServletFileUpload(factory);
                List<FileItem> fileItems = fileUpload.parseRequest(request);

                for (FileItem item : fileItems) {
                    //當前表單是否是文件表單
                    if (item.isFormField()) {
                        //獲取表單字段
                        String fieldName = item.getFieldName();
                        //必須使用 utf-8 編碼獲取表單字段的值,否則就是亂碼
                        String fieldValue = item.getString("utf-8");
                        System.out.println("表單字段:" + fieldName + "," + fieldValue);
                    } else {
                        //此 item 表示文件
                        //獲取文件的表單字段名稱
                        String fieldName = item.getFieldName();
                        //獲取文件的原始文件名,不包含路徑
                        String fileName = item.getName();
                        System.out.println("上傳文件:" + fieldName + "," + fileName);
                        //這里的 StringUtils 是 apache 的 commons-lang3 組件提供的實用工具類
                        if(StringUtils.isNotEmpty(fileName)) {
                            //將上傳的文件,以原始名稱保存到具體的目錄下,
                            //當然你可以生成隨機 uuid 給文件命名
                            //這里使用原始文件名,主要演示由於設置了過濾器統一處理字符編碼,
                            //所以保存中文文件名,不出現亂碼的問題
                            item.write(new File(dir, fileName));
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        //由於過濾器統一設置了響應編碼,
        //所以響應頁面中展示的中文,不會出現亂碼
        resp.getWriter().write("恭喜你,成功了");
    }
}

需要注意:由於本 demo 示例采用了 tomcat 插件,因此運行的是源碼本身的網站上傳文件,文件會保存到 webapp 下的 upload 文件夾下,在 idea 工具中可以立刻看到。如果你使用的是獨立的 tomcat 進行部署訪問時,上傳文件后需要到獨立部署的目標 webapp 下的 upload 文件夾下去查看上傳的文件。

下面列出 downLoadServlet 的內容,這個是專門用來處理下載文件請求的,具體細節如下:

package com.jobs.controller;

import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

//配置訪問地址
@WebServlet("/api/download")
public class downLoadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req,
                   HttpServletResponse response) throws ServletException, IOException {
        String parm = req.getParameter("downLoadName");
        String downLoadName;
        if (StringUtils.isNotBlank(parm)) {
            downLoadName = parm + ".txt";
        } else {
            downLoadName = "測試文件.txt";
        }

        //獲取所要下載的文件,這里只是 demo 演示,因此對下載的目標文件進行了硬編碼
        String dir = this.getServletContext().getRealPath("upload");
        File f = new File(dir, "測試文件.txt");

        //下載文件的響應類型,這里統一設置成了文件流
        //你可以根據自己所提供下載的文件類型,使用不同的響應 mime 類型
        response.setContentType("application/octet-stream;charset=utf-8");
        //設置下載彈出框中默認顯示的文件名稱,如果指定中文名稱的話,需要轉成 iso8859-1 編碼,解決亂碼問題
        String fileName = new String(downLoadName.getBytes(), "iso8859-1");
        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);

        //第一種下載方式,適合文件已經在硬盤上,此時讀取文件字節流,直接下載
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f))) {
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] bArr = new byte[1024];
            int len;
            while ((len = bis.read(bArr)) != -1) {
                outputStream.write(bArr, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        /*
        //第二種下載方式,適合不在硬盤上生成文件,而是在內存中生成文件字節流,然后下載
        //當然這里的例子,還是從硬盤上讀取了文件,但是將字節流存入 ByteArrayOutputStream 內存字節流
        //模擬使用 ByteArrayOutputStream 生成文件流,然后進行下載
        ServletOutputStream outputStream = response.getOutputStream();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
                ByteArrayOutputStream baos=new ByteArrayOutputStream()){

            byte[] bArr = new byte[1024];
            int len;
            while ((len = bis.read(bArr)) != -1) {
                baos.write(bArr, 0, len);
            }

            baos.writeTo(outputStream);
        } catch(IOException e){
            e.printStackTrace();
        }

        outputStream.flush();
        */
    }
}

本 demo 下載的是 webapp 下的 upload 文件夾下的固定文件:測試文件.txt

本 demo 提供了兩種下載的方式,第一種是讀取硬盤中的文件,采用響應流進行下載。
第二種方式是模擬不生成硬盤文件的前提下,采用內存流的方式生成文件,並最終寫入響應流,還是依靠響應流進行下載。
當然本 demo 還是從硬盤中讀取了文件,生成該文件的內存流。在實際開發中,我們可能會從數據庫中獲取記錄,然后在內存中生成 Excel 並寫入內存流,然后通過響應流進行下載 Excel 文件。

最后展示 index.html 頁面的具體內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上傳文件測試</title>
</head>
<body>
<form id="Form1" action="/api/upload" method="post" enctype="multipart/form-data">
    <table>
        <caption>表單提交,文件上傳,支持中文文件名稱</caption>
        <tr>
            <td>請輸入內容:</td>
            <td><input type="text" name="submitName"></td>
        </tr>
        <tr>
            <td>請選擇文件01:</td>
            <td><input type="file" name="upFile1"></td>
        </tr>
        <tr>
            <td>請選擇文件02:</td>
            <td><input type="file" name="upFile2"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <button type="submit">提交</button>
                <button type="reset">重置</button>
            </td>
        </tr>
    </table>
</form>
<br/>
<form id="Form2" action="/api/download" method="post">
    <table>
        <caption>文件下載,支持中文文件名稱</caption>
        <tr>
            <td>請輸入下載后的文件命名(不需要輸入后綴名):</td>
            <td><input type="text" name="downLoadName"></td>
        </tr>
        <tr>
            <td>點擊按鈕下載中文名稱的文件:</td>
            <td>
                <button type="submit">下載測試文件</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

該頁面的界面如下所示:

image

對於上傳文件的表單,文本框中可以錄入中文,可以同時上傳 2 個文件,上傳所選擇的文件也可以是中文名稱的文件。服務器接收到的表單字段內容,以及文件名稱,可以正常接收到中文,不會出現亂碼。對於下載文件的表單,可以在文本框中錄入你想看到的下載文件名稱,可以是中文,不會出現亂碼問題。

本 demo 示例的代碼下載地址為:https://files.cnblogs.com/files/blogs/699532/fileUpDownTest.zip




免責聲明!

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



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