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

Java HttpClient 實現表單提交與多文件上傳完整封裝

     編輯:Java 相關知識

技術棧

Apache HttpClient 4.5.x(經典版,非 5.x),支持 multipart/form-data 標準表單編碼,可同時上傳文件與普通字段。

Maven 依賴

<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>

核心工具類:HttpClientUtil

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);
    }
}

服務端接收(Servlet 3.0+ 標準 API,無需 Commons FileUpload)

@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\":\"上傳成功\"}");
    }
}

Android 端特殊說明

  • Android 6.0+ 移除了 HttpClient,需在 build.gradle 添加:
    android { useLibrary 'org.apache.http.legacy' }
  • 或遷移至 OkHttp(推薦),支持同樣的 MultipartBody
  • 文件路徑需用 Context.getExternalFilesDir() 等合規 API 獲取

進階優化點

  1. 連接池復用:生產環境用 PoolingHttpClientConnectionManager 管理連接
  2. 超時配置:RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(30000).build()
  3. 大文件流式:FileBody 內部已流式讀取,不佔用堆內存
  4. 進度監聽:需自定義 HttpEntityWrapper 包裝輸出流,回調進度

總結

HttpClient 4.x + httpmime 提供了標準、穩健的 multipart/form-data 實現。核心在於 MultipartEntityBuilder 靈活組裝字段與文件,配合 Servlet 3.0+ @MultipartConfig 實現零依賴服務端接收。Android 端建議遷移 OkHttp,服務端建議引入連接池與超時策略。

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