| 套件 | 核心類 | 狀態 | 建議 |
org.apache.commons.httpclient | HttpClient, PostMethod, GetMethod | Legacy 3.x | 維舊專案用 |
org.apache.http.client | CloseableHttpClient, HttpPost, HttpGet | Modern 4.5+/5.x | 新專案首選 |
一、文件上傳:四種寫法對比
1. 舊版 PostMethod + FilePart (multipart/form-data)
// Commons HttpClient 3.x
public void uploadLegacy(String localFile) throws Exception {
HttpClient client = new HttpClient();
PostMethod post = new PostMethod(UPLOAD_URL);
post.setParameter("userName", "scott");
post.setParameter("passwd", "123456");
Part[] parts = { new FilePart("file", new File(localFile)) };
post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
client.executeMethod(post);
// 處理回應...
post.releaseConnection();
}
2. 新版 HttpPost + FileBody (MultipartEntityBuilder)
// HttpClient 4.5+
public void uploadModern(String localFile) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(UPLOAD_URL);
FileBody fileBody = new FileBody(new File(localFile));
StringBody user = new StringBody("scott", ContentType.TEXT_PLAIN);
StringBody pass = new StringBody("123456", ContentType.TEXT_PLAIN);
HttpEntity entity = MultipartEntityBuilder.create()
.addPart("file", fileBody)
.addPart("userName", user)
.addPart("pass", pass)
.build();
post.setEntity(entity);
try (CloseableHttpResponse resp = client.execute(post)) {
System.out.println(EntityUtils.toString(resp.getEntity()));
}
}
}
3. 服務端通用處理 (Servlet 3.0+)
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String uploadPath = getServletContext().getRealPath("/") + "uploads/";
File dir = new File(uploadPath); if (!dir.exists()) dir.mkdirs();
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
factory.setSizeThreshold(1024 * 1024); // 1MB 記憶體閾值
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(50 * 1024 * 1024);
upload.setSizeMax(50 * 1024 * 1024);
upload.setHeaderEncoding("UTF-8");
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
if (item.isFormField()) {
System.out.println("表單欄位: " + item.getFieldName() + "=" + item.getString("UTF-8"));
} else {
String fileName = new File(item.getName()).getName(); // 防路徑遍歷
item.write(new File(uploadPath, fileName));
}
}
resp.setHeader("token", "ok");
}
}
二、文件下載:三種寫法對比
1. 舊版 GetMethod
public void downloadLegacy(String remoteName, String localFile) throws Exception {
HttpClient client = new HttpClient();
GetMethod get = new GetMethod(DOWNLOAD_URL);
get.setRequestHeader("fileName", remoteName);
get.setRequestHeader("userName", "scott");
if (client.executeMethod(get) == HttpStatus.SC_OK) {
try (FileOutputStream fos = new FileOutputStream(localFile)) {
fos.write(get.getResponseBody());
}
}
get.releaseConnection();
}
2. 新版 HttpGet (推薦)
public void downloadModern(String remoteName, String localFile) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse resp = client.execute(buildGet(remoteName));
InputStream in = resp.getEntity().getContent();
FileOutputStream fos = new FileOutputStream(localFile)) {
HttpEntity entity = resp.getEntity();
long len = entity.getContentLength();
if (len <= 0) { System.out.println("檔案不存在"); return; }
byte[] buf = new byte[8192];
int read;
while ((read = in.read(buf)) != -1) fos.write(buf, 0, read);
}
}
private HttpGet buildGet(String fileName) {
HttpGet get = new HttpGet(DOWNLOAD_URL);
get.addHeader("fileName", fileName);
get.addHeader("userName", "scott");
return get;
}
3. 原生 HttpURLConnection (零依賴)
public void downloadNative(String remoteName, String localFile) throws Exception {
URL url = new URL(DOWNLOAD_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("fileName", remoteName);
conn.setRequestProperty("userName", "scott");
conn.setConnectTimeout(60000);
conn.setReadTimeout(60000);
conn.connect();
try (InputStream in = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(localFile)) {
byte[] buf = new byte[8192];
int read;
while ((read = in.read(buf)) != -1) fos.write(buf, 0, read);
}
}
4. 服務端下載處理 (支援 Range 斷點續傳基礎)
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fileName = req.getHeader("fileName"); // 或 getParameter
File file = new File(DOWNLOAD_DIR, fileName);
if (!file.exists()) { resp.sendError(404); return; }
resp.setContentType("application/octet-stream");
resp.setHeader("Content-Disposition", "attachment; filename=" + fileName);
resp.setContentLengthLong(file.length());
resp.setHeader("Accept-Ranges", "bytes");
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(resp.getOutputStream())) {
byte[] buf = new byte[8192];
int len;
while ((len = bis.read(buf)) != -1) bos.write(buf, 0, len);
}
}
}
三、核心最佳實踐總結
- 新專案強制用 4.5+/5.x:
CloseableHttpClient 實作 AutoCloseable,配合 try-with-resources 無洩漏。
- 連線池複用:單例
PoolingHttpClientConnectionManager,避免頻繁建立 TCP 連線。
- 超時配置:
RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(30000).build()。
- 大檔流式:上傳用
MultipartEntityBuilder、下載用 resp.getEntity().getContent() 邊讀邊寫,嚴禁 getResponseBody() 全讀入記憶體。
- 釋放連線:舊版
releaseConnection()、新版 resp.close() / EntityUtils.consume(entity) 必呼叫。
- 錯誤處理:統一捕獲
IOException、HttpException,記錄完整堆疊並拋出業務異常。
總結
HttpClient 文件上傳下載核心在於 MultipartEntityBuilder 組裝請求體 與 流式處理回應體。掌握新舊 API 差異、連線池複用、超時與資源釋放三大支柱,即能構建穩健的 HTTP 客戶端。現代專案建議直接使用 HttpClient 5.x + Reactive Streams (HttpClient Fluent API) 進一步簡化代碼。
- 上一篇文章: Java 文件下載完整實戰:四種核心方式、在線預覽與斷點續傳考量
- 下一篇文章: Java 正則表達式提取並替換 HTML 標籤屬性值:實戰模式與代碼詳解
Copyright ©
程式碼之家 All Rights Reserved