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

Java Apache Commons FileUpload 實現檔案上傳下載完整教學

     編輯:Java 相關知識

前言

commons-fileupload 是 Java Web 開發中處理 multipart/form-data 表單上傳的經典元件。本文以完整 Maven 風格專案結構,示範檔案上傳、下載、資料庫存儲的端到端實作。

核心依賴

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version> <!-- 請用最新版 -->
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

1. 實體類:Upfile

public class Upfile {
    private String id;           // UUID 主鍵
    private String uuidName;     // 隨機檔名
    private String fileName;     // 原始檔名
    private String savePath;     // 伺服器儲存路徑
    private Timestamp uploadTime;
    private String description;
    private String username;
    // Getter/Setter/建構子 略
}

2. 上傳頁面 (upload.jsp)

<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    <input type="text" name="username" placeholder="用戶名" />
    <input type="file" name="file" />
    <textarea name="description" placeholder="描述"></textarea>
    <button type="submit">上傳</button>
</form>

關鍵: enctype="multipart/form-data" 且 method 必須為 POST。

3. 核心工具類:WebUtil(封裝上傳解析)

public class WebUtil {
    // 單檔 20MB、總計 50MB 限制
    private static final long FILE_SIZE_MAX = 20 * 1024 * 1024;
    private static final long TOTAL_SIZE_MAX = 50 * 1024 * 1024;

    public static Upfile upload(HttpServletRequest request) 
            throws FileSizeLimitExceededException, SizeLimitExceededException {
        
        Upfile upfile = new Upfile();
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(1024 * 1024); // 1MB 記憶體緩衝
        factory.setRepository(new File(request.getServletContext().getRealPath("/tmp")));

        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setHeaderEncoding("UTF-8");
        upload.setFileSizeMax(FILE_SIZE_MAX);
        upload.setSizeMax(TOTAL_SIZE_MAX);

        List<FileItem> items = upload.parseRequest(request);
        for (FileItem item : items) {
            if (item.isFormField()) { // 普通表單欄位
                String val = item.getString("UTF-8");
                if ("username".equals(item.getFieldName())) upfile.setUsername(val);
                else if ("description".equals(item.getFieldName())) upfile.setDescription(val);
            } else { // 檔案欄位
                String origName = item.getName().substring(item.getName().lastIndexOf("\\") + 1);
                String uuidName = UUID.randomUUID().toString().replace("-", "") + "_" + origName;
                String basePath = request.getServletContext().getRealPath("/WEB-INF/upload");
                String savePath = generateRandomDir(basePath, uuidName); // 雜湊分目錄
                
                try (InputStream in = item.getInputStream();
                     OutputStream out = new FileOutputStream(new File(savePath, uuidName))) {
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
                }
                upfile.setId(UUID.randomUUID().toString());
                upfile.setUuidName(uuidName);
                upfile.setFileName(origName);
                upfile.setSavePath(savePath);
                upfile.setUploadTime(new Timestamp(System.currentTimeMillis()));
                item.delete(); // 清理暫存
            }
        }
        return upfile;
    }

    // 雜湊分目錄,避免單目錄檔案過多
    private static String generateRandomDir(String base, String uuidName) {
        int hash = uuidName.hashCode();
        StringBuilder sb = new StringBuilder();
        while (hash > 0) {
            sb.append('/').append(hash & 0xF);
            hash >>= 4;
        }
        String path = base + sb;
        new File(path).mkdirs();
        return path;
    }
}

4. 上傳 Servlet

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        if (!ServletFileUpload.isMultipartContent(req)) {
            req.setAttribute("msg", "請設定 enctype=multipart/form-data");
            req.getRequestDispatcher("/upload.jsp").forward(req, resp);
            return;
        }
        try {
            Upfile upfile = WebUtil.upload(req);
            new UpfileServiceImpl().add(upfile); // 持久化到 DB
            req.setAttribute("msg", "上傳成功");
        } catch (FileSizeLimitExceededException e) {
            req.setAttribute("msg", "單檔超過 20MB 限制");
        } catch (SizeLimitExceededException e) {
            req.setAttribute("msg", "總大小超過 50MB 限制");
        } catch (Exception e) {
            req.setAttribute("msg", "上傳失敗:" + e.getMessage());
            e.printStackTrace();
        }
        req.getRequestDispatcher("/upload.jsp").forward(req, resp);
    }
}

5. 下載 Servlet

@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String id = req.getParameter("id");
        Upfile upfile = new UpfileServiceImpl().findById(id);
        if (upfile == null) { resp.sendError(404); return; }

        File file = new File(upfile.getSavePath(), upfile.getUuidName());
        if (!file.exists()) { resp.sendError(404, "檔案已遺失"); return; }

        resp.setContentType("application/octet-stream");
        resp.setHeader("Content-Disposition", 
            "attachment; filename=\"" + URLEncoder.encode(upfile.getFileName(), "UTF-8") + "\"");
        resp.setContentLengthLong(file.length());

        try (InputStream in = new FileInputStream(file);
             OutputStream out = resp.getOutputStream()) {
            byte[] buf = new byte[8192];
            int len;
            while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
        }
    }
}

6. 資料庫建表 SQL (MySQL)

CREATE TABLE upfiles (
    id VARCHAR(100) PRIMARY KEY,
    uuid_name VARCHAR(255),
    file_name VARCHAR(255),
    save_path VARCHAR(500),
    upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    description VARCHAR(500),
    username VARCHAR(50)
);

關鍵技術點總結

  1. 分目錄儲存:以 UUID 雜湊值建立多層子目錄,避免單目錄檔案過多導致檔案系統效能下降。
  2. 暫存清理:DiskFileItemFactory 設定閾值,超過寫入磁碟暫存區,上傳完成後呼叫 item.delete() 釋放空間。
  3. 編碼處理:upload.setHeaderEncoding("UTF-8") 解決中文檔名亂碼;下載時 URLEncoder.encode 處理回應標頭。
  4. 大小限制:同時設定單檔與總大小上限,防止惡意上傳佔滿磁碟。
  5. 資料庫分離:僅存元資料,實體檔案落地磁碟,利於備份與擴展。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved