本文提供兩種 Java 文件上傳實現方案:基礎 Servlet 上傳與支援斷點續傳的 AJAX 分片上傳,涵蓋後端 Servlet、前端 JavaScript 完整代碼。
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();
}
}
}
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("上傳失敗"); }
});
}
<input type="file" id="upload" />
<input type="button" value="上傳" onclick="test()" />
<form id="output" enctype="multipart/form-data"></form>
file.slice(start, end) 切片start 位置參數append 模式寫入 (new FileOutputStream(file, true))var chunk = file.slice(startByte, endByte);
// 參數:起始位元組(含)、結束位元組(不含)
// 回傳新的 Blob 物件,不複製資料,效能極佳
FileOutputStream fos = new FileOutputStream(serverFile, true); // true = append 模式
IOUtils.copy(inputStream, fos);
方案一適合中小檔案快速接入,代碼簡潔;方案二支援大檔案斷點續傳、暫停恢復,符合現代 Web 應用需求。實際專案建議結合 Plupload、WebUploader 等成熟前端庫,後端搭配資料庫記錄分片狀態,構建企業級上傳系統。