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

Java 文件上傳下載完整實戰:Commons FileUpload + Servlet 3.0 雙方案

     編輯:Java 相關知識

前端表單關鍵點

<form action="fileServlet?method=upload" method="post" enctype="multipart/form-data">
    用戶名:<input type="text" name="userName"><br/>
    文件:<input type="file" name="file_img"><br/>
    <input type="submit" value="提交">
</form>
  • method="post":大數據必須 POST
  • enctype="multipart/form-data":二進制流編碼,非默認 application/x-www-form-urlencoded

方案一:Apache Commons FileUpload(經典、兼容 Servlet 2.x)

依賴

<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>

核心 Servlet 代碼

@WebServlet("/fileServlet")
public class FileServlet extends HttpServlet {
    private static final String UPLOAD_DIR = "upload";

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String method = req.getParameter("method");
        if ("upload".equals(method)) upload(req, resp);
        else if ("downList".equals(method)) downList(req, resp);
        else if ("down".equals(method)) download(req, resp);
    }

    private void upload(HttpServletRequest req, HttpServletResponse resp) {
        try {
            // 1. 工廠 & 核心對象
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(10 * 1024 * 1024);   // 10MB 單文件
            upload.setSizeMax(50 * 1024 * 1024);       // 50MB 總大小
            upload.setHeaderEncoding("UTF-8");         // 解決中文文件名亂碼

            // 2. 解析請求
            List<FileItem> items = upload.parseRequest(req);
            for (FileItem item : items) {
                if (item.isFormField()) { // 普通字段
                    String val = item.getString("UTF-8");
                    System.out.println(item.getFieldName() + "=" + val);
                } else { // 文件字段
                    String fileName = item.getName();
                    // 防重名:UUID + 原文件名
                    String savedName = UUID.randomUUID() + "#" + fileName;
                    String savePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
                    File dir = new File(savePath);
                    if (!dir.exists()) dir.mkdirs();
                    File dest = new File(dir, savedName);
                    item.write(dest);      // 寫入磁盤
                    item.delete();         // 刪除臨時文件
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void downList(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String basePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
        File dir = new File(basePath);
        String[] names = dir.list();
        Map<String, String> map = new LinkedHashMap<>(); // 保留順序
        if (names != null) {
            for (String full : names) {
                String shortName = full.substring(full.lastIndexOf("#") + 1);
                map.put(full, shortName);
            }
        }
        req.setAttribute("fileNames", map);
        req.getRequestDispatcher("/downlist.jsp").forward(req, resp);
    }

    private void download(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String fileName = req.getParameter("fileName");
        fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8"); // GET 參數解碼
        String basePath = getServletContext().getRealPath("/") + UPLOAD_DIR;
        File file = new File(basePath, fileName);
        if (!file.exists()) {
            resp.sendError(404);
            return;
        }
        // 設置下載頭
        String encodedName = URLEncoder.encode(fileName.substring(fileName.lastIndexOf("#") + 1), "UTF-8");
        resp.setHeader("Content-Disposition", "attachment; filename=" + encodedName);
        resp.setContentType("application/octet-stream");
        try (InputStream in = new FileInputStream(file);
             OutputStream out = resp.getOutputStream()) {
            byte[] buf = new byte[4096];
            int len;
            while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
        }
    }
}

方案二:Servlet 3.0 原生 API(無第三方依賴,Tomcat 7+)

@MultipartConfig(
    location = "/tmp",           // 臨時文件目錄
    fileSizeThreshold = 1024*1024, // 1MB 以上寫磁盤
    maxFileSize = 10*1024*1024,   // 10MB
    maxRequestSize = 50*1024*1024 // 50MB
)
@WebServlet("/fileServlet3")
public class FileServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // 獲取 Part
        Part filePart = req.getPart("file_img");
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
        String savedName = UUID.randomUUID() + "#" + fileName;
        String savePath = getServletContext().getRealPath("/upload") + File.separator + savedName;
        filePart.write(savePath); // 自動處理臨時文件移動
        filePart.delete();
        // 普通字段
        String userName = req.getParameter("userName");
    }
}

關鍵難點與最佳實踐

難點解決方案
中文文件名亂碼FileUpload: setHeaderEncoding("UTF-8");Servlet 3.0: 容器需配置 URIEncoding="UTF-8"
文件名衝突UUID + 原文件名,或 MD5 + 擴展名
大文件內存溢出FileUpload: DiskFileItemFactory 閾值;Servlet 3.0: fileSizeThreshold
下載中文文件名URLEncoder.encode(name, "UTF-8") + Content-Disposition: attachment; filename=xxx
斷點續傳/秒傳前端分片,後端 RandomAccessFile.seek() 追寫,維護分片索引
安全校驗限制副檔名、MIME 類型、掃毒、存儲非 Web 目錄

JSP 列表頁

<c:forEach items="${fileNames}" var="entry">
    <a href="fileServlet?method=down&fileName=${entry.key}">${entry.value}</a><br/>
</c:forEach>

總結

Commons FileUpload 兼容性好、控制力強,適合舊項目維護;Servlet 3.0 @MultipartConfig 零依賴、代碼極簡,是新項目首選。無論哪種方案,文件名去重、大小限制、編碼處理、安全校驗、臨時文件清理 是生產級上傳下載的五大基石。

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