Apache HttpClient 4.5.x(經典版,非 5.x),支持 multipart/form-data 標準表單編碼,可同時上傳文件與普通字段。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.14</version> <!-- 關鍵:multipart 支持 -->
</dependency>
import org.apache.http.*;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.*;
import org.apache.http.entity.mime.content.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.Serializable;
import java.util.*;
public class HttpClientUtil {
/** 表單數據載體 */
public static class MultipartForm implements Serializable {
private String action;
private String method = "POST";
private final Map<String, String> normalFields = new LinkedHashMap<>();
private final Map<String, File> fileFields = new LinkedHashMap<>();
public MultipartForm(String action) { this.action = action; }
public void addField(String key, String value) { normalFields.put(key, value); }
public void addFile(String key, File file) { fileFields.put(key, file); }
// Getters 省略
}
/** 提交表單並返回響應字符串 */
public static String submit(MultipartForm form) {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(form.getAction());
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setCharset(StandardCharsets.UTF_8);
// 添加普通字段
for (Map.Entry<String, String> e : form.getNormalFields().entrySet()) {
builder.addPart(e.getKey(),
new StringBody(e.getValue(), ContentType.create("text/plain", StandardCharsets.UTF_8)));
}
// 添加文件字段
for (Map.Entry<String, File> e : form.getFileFields().entrySet()) {
builder.addPart(e.getKey(), new FileBody(e.getValue()));
}
post.setEntity(builder.build());
try (CloseableHttpResponse response = client.execute(post)) {
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status == 200 && entity != null) {
return EntityUtils.toString(entity, StandardCharsets.UTF_8);
}
throw new RuntimeException("HTTP " + status + ": " + EntityUtils.toString(entity));
}
} catch (Exception e) {
throw new RuntimeException("表單提交失敗", e);
}
}
}
public class UploadDemo {
public static void main(String[] args) {
MultipartForm form = new MultipartForm("https://localhost:8080/upload");
// 普通字段
form.addField("username", "張三");
form.addField("token", "abc123");
// 多文件上傳
form.addFile("avatar", new File("d:/avatar.jpg"));
form.addFile("attach", new File("d:/doc.pdf"));
form.addFile("image2", new File("d:/pic2.png"));
String response = HttpClientUtil.submit(form);
System.out.println("服務端響應: " + response);
}
}
@WebServlet("/upload")
@MultipartConfig(
maxFileSize = 1024 * 1024 * 10, // 10MB
maxRequestSize = 1024 * 1024 * 50, // 50MB
fileSizeThreshold = 1024 * 1024 // 1MB 寫入臨時文件
)
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=UTF-8");
// 普通字段
String username = req.getParameter("username");
String token = req.getParameter("token");
// 文件處理
Collection<Part> fileParts = req.getParts().stream()
.filter(p -> p.getSubmittedFileName() != null)
.collect(Collectors.toList());
for (Part part : fileParts) {
String fileName = Paths.get(part.getSubmittedFileName()).getFileName().toString();
String savePath = "D:/uploads/" + System.currentTimeMillis() + "_" + fileName;
part.write(savePath); // 自動處理內存/臨時文件
System.out.println("保存文件: " + savePath);
}
resp.getWriter().write("{\"code\":200,\"msg\":\"上傳成功\"}");
}
}
build.gradle 添加:android { useLibrary 'org.apache.http.legacy' }OkHttp(推薦),支持同樣的 MultipartBodyContext.getExternalFilesDir() 等合規 API 獲取PoolingHttpClientConnectionManager 管理連接RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(30000).build()FileBody 內部已流式讀取,不佔用堆內存HttpEntityWrapper 包裝輸出流,回調進度HttpClient 4.x + httpmime 提供了標準、穩健的 multipart/form-data 實現。核心在於 MultipartEntityBuilder 靈活組裝字段與文件,配合 Servlet 3.0+ @MultipartConfig 實現零依賴服務端接收。Android 端建議遷移 OkHttp,服務端建議引入連接池與超時策略。