首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaWeb 文件上傳下載完整實現指南

     編輯:Java 相關知識

前言

文件上傳下載是 Web 開發中最基礎也最常見的功能之一。本文將從原理分析、核心組件使用、常見問題處理到完整代碼實現,系統講解 JavaWeb 中文件上傳下載的完整方案。

一、文件上傳原理分析

1. 表單提交的必要條件

實現文件上傳,HTML 表單必須滿足三個條件:

  • method="post":只能使用 POST 方式提交
  • enctype="multipart/form-data":編碼類型必須設置為 multipart/form-data
  • <input type="file">:必須包含文件類型的輸入域

2. enctype 屬性詳解

編碼類型適用場景服務端獲取方式
application/x-www-form-urlencoded (默認) 普通表單數據 request.getParameter("name")
multipart/form-data 包含文件上傳 需解析請求輸入流

當使用 multipart/form-data 時,請求體不再是簡單的鍵值對,而是包含邊界分隔符的多部分數據,原生的 getParameter 方法將失效,必須解析 request.getInputStream()。

二、Apache Commons FileUpload 組件使用

1. 依賴包

  • commons-fileupload.jar - 核心包
  • commons-io.jar - 依賴包

2. 核心類

  • DiskFileItemFactory:創建 FileItem 對象的工廠
  • ServletFileUpload:核心解析器,負責解析請求
  • FileItem:代表一個表單項(普通字段或文件字段)

3. 基本使用步驟

// 1. 創建工廠對象
DiskFileItemFactory factory = new DiskFileItemFactory();

// 2. 創建解析器
ServletFileUpload fileUpload = new ServletFileUpload(factory);

// 3. 解析請求
List<FileItem> list = fileUpload.parseRequest(request);

// 4. 遍歷處理
for (FileItem item : list) {
    if (item.isFormField()) {
        // 處理普通字段
        String fieldName = item.getFieldName();
        String fieldValue = item.getString("UTF-8");
    } else {
        // 處理文件字段
        String fileName = item.getName();
        InputStream is = item.getInputStream();
        item.write(new File("保存路徑"));
    }
}

4. FileItem 常用方法

方法說明
isFormField()判斷是否為普通表單字段
getFieldName()獲取字段名稱
getString()獲取字段內容
getName()獲取上傳文件名
getContentType()獲取文件 MIME 類型
getSize()獲取文件大小(字節)
getInputStream()獲取文件輸入流
write(File)保存文件到指定路徑
delete()刪除臨時文件

三、文件上傳實戰中的關鍵問題與解決方案

1. 服務器安全性

將上傳文件保存目錄設置在 Web 根目錄之外(如 WEB-INF 下),防止用戶直接通過 URL 訪問上傳的文件。

2. 文件名衝突避免

使用 UUID + 原文件名的方式生成唯一文件名:

String filename = UUID.randomUUID().toString() + "_" + FilenameUtils.getName(originalName);

3. 目錄分散存儲(避免單目錄文件過多)

方案一:按日期分目錄

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateDir = sdf.format(new Date());

方案二:按文件名 HashCode 分級目錄


private String makeChildDirectory(String filename) {
    int hashcode = filename.hashCode();
    String hex = Integer.toHexString(hashcode);
    // 取前兩位作為二級目錄
    return hex.charAt(0) + File.separator + hex.charAt(1);
}

4. 文件大小限制


// 單個文件最大 3MB
fileUpload.setFileSizeMax(1024 * 1024 * 3);
// 總大小最大 6MB
fileUpload.setSizeMax(1024 * 1024 * 6);

5. 臨時文件清理

DiskFileItemFactory 默認使用系統臨時目錄,文件超過 10KB 時寫入磁盤。處理完成後必須調用 fileItem.delete() 清理臨時文件。

6. 編碼問題處理

// 設置請求頭編碼,解決文件名中文亂碼
fileUpload.setHeaderEncoding("UTF-8");

四、完整上傳 Servlet 實現

public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // 1. 判斷是否支持文件上傳
        if (!ServletFileUpload.isMultipartContent(request)) {
            throw new RuntimeException("表單必須設置 enctype=multipart/form-data");
        }

        // 2. 配置工廠與解析器
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setRepository(new File("f:\\temp")); // 指定臨時目錄
        
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setHeaderEncoding("UTF-8");
        upload.setFileSizeMax(1024 * 1024 * 3); // 3MB
        upload.setSizeMax(1024 * 1024 * 10);    // 10MB

        try {
            List<FileItem> items = upload.parseRequest(request);
            for (FileItem item : items) {
                if (item.isFormField()) {
                    processFormField(item);
                } else {
                    processUploadField(item);
                }
            }
        } catch (FileUploadBase.FileSizeLimitExceededException e) {
            response.getWriter().write("單個文件不能超過 3MB");
        } catch (FileUploadBase.SizeLimitExceededException e) {
            response.getWriter().write("總文件大小不能超過 10MB");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void processUploadField(FileItem item) throws Exception {
        String filename = FilenameUtils.getName(item.getName());
        if (filename == null || filename.isEmpty()) return;

        // 生成唯一文件名
        filename = UUID.randomUUID() + "_" + filename;
        
        // 目錄分散
        String childDir = makeChildDirectory(uploadDir, filename);
        
        // 保存文件(自動清理臨時文件)
        File saveFile = new File(uploadDir, childDir + File.separator + filename);
        item.write(saveFile);
        item.delete();
    }

    private void processFormField(FileItem item) throws Exception {
        String name = item.getFieldName();
        String value = item.getString("UTF-8");
        System.out.println(name + " = " + value);
    }
}

五、動態多文件上傳前端實現

通過 JavaScript 動態添加/刪除文件輸入框:

<script>
function addFile() {
    var div = document.getElementById("fileContainer");
    div.innerHTML += "<div><input type='file' name='photo' />" +
                     "<input type='button' value='刪除' onclick='delFile(this)'/></div>";
}
function delFile(btn) {
    btn.parentNode.parentNode.removeChild(btn.parentNode);
}
</script>

<form enctype="multipart/form-data" action="/upload" method="post">
    <div id="fileContainer">
        <div><input type="file" name="photo" />
        <input type="button" value="添加" onclick="addFile()" /></div>
    </div>
    <input type="submit" value="上傳" />
</form>

六、文件下載實現

核心要點:設置正確的響應頭,處理中文文件名編碼兼容性。

public class DownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        String filename = "銷售報表.csv";
        String userAgent = request.getHeader("User-Agent").toLowerCase();
        
        // 處理中文文件名編碼(兼容 IE 和現代瀏覽器)
        if (userAgent.contains("msie") || userAgent.contains("trident")) {
            filename = URLEncoder.encode(filename, "UTF-8");
        } else {
            filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
        }

        // 關鍵響應頭
        response.setHeader("Content-Disposition", "attachment;filename=" + filename);
        response.setContentType(getServletContext().getMimeType(filename));
        response.setCharacterEncoding("UTF-8");

        // 輸出文件內容
        try (PrintWriter out = response.getWriter()) {
            out.write("商品,銷量\n");
            out.write("電視機,20\n");
            out.write("洗衣機,10\n");
            out.write("冰箱,8\n");
        }
    }
}

七、總結

文件上傳下載看似簡單,但生產環境中需考慮:安全性、性能、編碼兼容、異常處理、臨時文件清理等多個維度。掌握 Commons FileUpload 核心原理,配合合理的文件命名、目錄分散、大小限制策略,即可構建健壯的文件處理模塊。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved