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

JavaWeb 實現檔案上傳即時進度顯示完整方案

     編輯:Java 相關知識

為什麼需要上傳進度顯示?

傳統表單提交上傳檔案時,頁面會整頁跳轉,使用者無法得知進度。隨著 Ajax 技術普及,異步上傳成為常態,但 Servlet 只有在檔案完全接收完畢才會回應,導致前端無法獲取即時進度。本文提供一套基於「單例模式 + 多執行緒 + 輪詢」的完整解決方案。

核心架構設計

將功能拆解為三大模組:

  1. 進度單例:線程安全的 `Hashtable` 儲存每個上傳任務的進度資訊
  2. 上傳 Servlet:處理檔案流讀取,即時更新進度到單例
  3. 進度查詢 Servlet:前端定時輪詢,從單例讀取並回傳 JSON 進度資料

關鍵程式碼實現

1. 進度單例

public class ProgressSingleton {
    private static Hashtable<Object, Object> table = new Hashtable<>();
    public static void put(Object key, Object value) { table.put(key, value); }
    public static Object get(Object key) { return table.get(key); }
    public static Object remove(Object key) { return table.remove(key); }
}

2. 上傳 Servlet 核心邏輯

@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(4 * 1024);
        ServletFileUpload upload = new ServletFileUpload(factory);
        
        List<FileItem> items = upload.parseRequest(new ServletRequestContext(request));
        FileItem fileItem = items.get(0);
        
        // 以 sessionId + 檔名為唯一鍵
        String key = request.getSession().getId() + fileItem.getName();
        
        // 初始化進度
        ProgressSingleton.put(key + "Size", fileItem.getSize());
        long progress = 0;
        
        // 以流方式讀取,每讀一塊更新進度
        try (InputStream in = fileItem.getInputStream();
             FileOutputStream out = new FileOutputStream("D:/test/" + fileItem.getName())) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) != -1) {
                progress += len;
                ProgressSingleton.put(key + "Progress", progress);
                out.write(buffer, 0, len);
            }
        } finally {
            // 完成後清理
            ProgressSingleton.remove(key + "Size");
            ProgressSingleton.remove(key + "Progress");
        }
        response.getWriter().print("done");
    }
}

3. 進度查詢 Servlet

@WebServlet("/ProgressServlet")
public class ProgressServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        String key = request.getSession().getId() + request.getParameter("filename");
        Object size = ProgressSingleton.get(key + "Size");
        Object progress = ProgressSingleton.get(key + "Progress");
        
        JSONObject json = new JSONObject();
        json.put("size", size == null ? 100 : size);
        json.put("progress", progress == null ? 0 : progress);
        response.getWriter().print(json.toString());
    }
}

4. 前端頁面與 Ajax 輪詢

<form id="dataForm" enctype="multipart/form-data">
    <input type="file" name="file" id="fileInput">
    <input type="submit" value="上傳" id="submit">
</form>
<div id="progress">0%</div>

<script>
$("#submit").click(function(e) {
    e.preventDefault();
    var form = document.getElementById("dataForm");
    var progressDiv = document.getElementById("progress");
    
    // 每 500ms 輪詢一次進度
    var timer = setInterval(function() {
        $.post('ProgressServlet', { filename: fileInput.files[0].name }, function(res) {
            var data = JSON.parse(res);
            progressDiv.innerText = parseInt((data.progress / data.size) * 100) + '%';
        });
    }, 500);
    
    // 異步上傳
    $.ajax({
        url: 'UploadServlet',
        type: 'POST',
        data: new FormData(form),
        processData: false,
        contentType: false,
        success: function() {
            clearInterval(timer);
            progressDiv.innerText = '100%';
        }
    });
});
</script>

效果展示

方案優缺點與改進方向

  • ✅ 實現簡單、無需第三方庫、相容舊版 Servlet 容器
  • ⚠️ 單例儲存進度,叢集環境下需改用 Redis 或資料庫共享
  • ⚠️ 輪詢頻率需平衡即時性與伺服器負載
  • 🚀 現代專案建議使用 HTML5 `XMLHttpRequest.upload.onprogress` 或 WebSocket 推送進度

這套方案雖然經典,但在微服務與前後端分離架構下,建議升級為前端原生進度事件或 WebSocket 即時推送,體驗更流暢、架構更乾淨。

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