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

Java 實現文件上傳與斷點續傳完整代碼方案

     編輯:Java 相關知識

Java 文件上傳完整方案:基礎上傳與斷點續傳雙重實現

本文提供兩種 Java 文件上傳實現方案:基礎 Servlet 上傳與支援斷點續傳的 AJAX 分片上傳,涵蓋後端 Servlet、前端 JavaScript 完整代碼。

方案一:基礎 Servlet 文件上傳

後端 AjaxUpload Servlet

package com.github.reston.servlet;

import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

@WebServlet("/AjaxUpload")
public class AjaxUpload extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        
        String basePath = getServletContext().getRealPath("/upload");
        File baseDir = new File(basePath);
        if (!baseDir.isDirectory()) baseDir.mkdirs();
        
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) return;
        
        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            
            @SuppressWarnings("unchecked")
            List<FileItem> items = upload.parseRequest(request);
            
            long startPos = 0;
            String fieldName = "";
            
            // 先解析表單欄位獲取 start 參數
            for (FileItem item : items) {
                if (item.isFormField() && "start".equals(item.getFieldName())) {
                    startPos = Long.parseLong(item.getString());
                }
            }
            
            // 處理檔案欄位
            for (FileItem item : items) {
                if (item.isFormField()) continue;
                
                fieldName = item.getFieldName();
                File serverFile = new File(basePath, fieldName);
                
                // 首次建立檔案
                if (serverFile.length() == 0) {
                    try (FileOutputStream fos = new FileOutputStream(serverFile, true)) {
                        IOUtils.copy(item.getInputStream(), fos);
                    }
                }
                
                // 斷點續傳:追加寫入
                if (startPos > 0) {
                    try (FileOutputStream fos = new FileOutputStream(serverFile, true)) {
                        IOUtils.copy(item.getInputStream(), fos);
                    }
                }
                
                // 回傳目前檔案大小
                PrintWriter pw = response.getWriter();
                pw.write("{\"length\":\"" + serverFile.length() + "\"}");
                pw.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

前端 JavaScript 斷點續傳核心邏輯

var ajaxupload = function(config) {
    var xhr = new XMLHttpRequest();
    var file = document.getElementById(config.id).files[0];
    var fileName = file.name;
    var fileSize = file.size;
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                config.success(xhr.responseText);
            } else {
                config.error(xhr.status, xhr.statusText);
            }
        }
    };
    
    xhr.open(config.method, config.url, true);
    
    var formData = new FormData();
    formData.append("name", fileName);
    formData.append("start", config.data.start);
    // 關鍵:使用 slice 切片上傳
    formData.append(fileName, file.slice(config.data.start, fileSize));
    
    xhr.send(formData);
};

// 使用範例:遞迴分片上傳
function test() {
    ajaxupload({
        id: "upload",
        url: "/PCC/reston/AjaxUpload",
        method: "POST",
        data: { start: 0 },
        success: function(response) {
            var uploaded = JSON.parse(response).length;
            if (uploaded < fileSize) {
                // 遞迴上傳下一片
                ajaxupload({
                    id: "upload",
                    url: "/PCC/reston/AjaxUpload",
                    method: "POST",
                    data: { start: uploaded },
                    success: function() { /* 完成 */ },
                    error: function() { /* 錯誤 */ }
                });
            }
        },
        error: function() { console.log("上傳失敗"); }
    });
}

HTML 頁面

<input type="file" id="upload" />
<input type="button" value="上傳" onclick="test()" />
<form id="output" enctype="multipart/form-data"></form>

方案二:HTML5 File API 斷點續傳進階實現

核心原理

  • 前端使用 file.slice(start, end) 切片
  • 每片上傳攜帶 start 位置參數
  • 後端以 append 模式寫入 (new FileOutputStream(file, true))
  • 前端根據回傳長度決定下一片起始位置
  • 支援暫停/恢復:記錄已上傳位置,重新整理後可繼續

關鍵技術點

1. Blob.slice() 方法
var chunk = file.slice(startByte, endByte);
// 參數:起始位元組(含)、結束位元組(不含)
// 回傳新的 Blob 物件,不複製資料,效能極佳
2. 伺服器端追加寫入
FileOutputStream fos = new FileOutputStream(serverFile, true); // true = append 模式
IOUtils.copy(inputStream, fos);
3. 斷點記錄策略
  • 記憶體:JS 變數記錄 (頁面重整即丟失)
  • localStorage:瀏覽器本地儲存上傳進度
  • 伺服器端:資料庫記錄檔案 ID、已上傳大小、分片資訊
  • 混合模式:localStorage 快速恢復 + 伺服器端權威驗證

生產環境增強建議

  • 分片大小:建議 1-5MB,平衡記憶體佔用與請求數
  • 併發控制:限制同時上傳分片數 (如 3 個),避免頻寬耗盡
  • 完整性校驗:各分片上傳後計算 MD5,最終合併驗證整體 MD5
  • 合併策略:所有分片上傳完成後,後端合併或前端通知合併
  • 錯誤重試:指數退避重試機制,失敗分片重新上傳
  • 秒傳功能:上傳前計算檔案 MD5,查詢伺服器是否已存在
  • 跨瀏覽器:IE10+ 支援 Blob.slice,舊版 IE 需用 msSaveBlob 或 Flash 替代

總結

方案一適合中小檔案快速接入,代碼簡潔;方案二支援大檔案斷點續傳、暫停恢復,符合現代 Web 應用需求。實際專案建議結合 Plupload、WebUploader 等成熟前端庫,後端搭配資料庫記錄分片狀態,構建企業級上傳系統。

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