Android 端無法依賴高層 HTTP 庫,需手工組裝 multipart/form-data 請求,透過 Socket 直接發送。
FormFilepublic class FormFile {
private byte[] data;
private InputStream inStream;
private File file;
private String fileName, parameterName, contentType = "application/octet-stream";
private String operType; // URL 額外查詢參數
// 建構子、getter/setter 略...
/** 執行上傳 */
public boolean post(String urlStr, Map params) throws Exception {
final String BOUNDARY = "--------------7da2137580612";
final String END_LINE = "--" + BOUNDARY + "--\r\n";
URL url = new URL(urlStr);
int port = url.getPort() == -1 ? 80 : url.getPort();
try (Socket socket = new Socket(InetAddress.getByName(url.getHost()), port);
OutputStream out = socket.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
// 1. 計算實體總長度
StringBuilder fileMeta = buildFileMeta(BOUNDARY);
StringBuilder textMeta = buildTextMeta(BOUNDARY, params);
int totalLen = fileMeta.length() + textMeta.length() +
(inStream != null ? (int)file.length() : data.length) + END_LINE.length();
// 2. 發送 HTTP 請求行與 Header
sendRequestLine(out, url, operType);
sendHeaders(out, totalLen, BOUNDARY);
// 3. 發送表單欄位
out.write(textMeta.toString().getBytes());
// 4. 發送檔案內容
out.write(fileMeta.toString().getBytes());
if (inStream != null) {
byte[] buf = new byte[1024];
int len;
while ((len = inStream.read(buf)) != -1) out.write(buf, 0, len);
} else {
out.write(data);
}
out.write("\r\n".getBytes());
// 5. 發送結束標記
out.write(END_LINE.getBytes());
out.flush();
// 6. 讀取響應判斷
String statusLine = in.readLine();
return statusLine != null && statusLine.contains("200");
}
}
// buildFileMeta, buildTextMeta, sendRequestLine, sendHeaders 等私有方法略...
}
| 部分 | 格式範例 |
|---|---|
| 請求行 | POST /path?action=xxx HTTP/1.1\r\n |
| Content-Type | multipart/form-data; boundary=----BOUNDARY\r\n |
| 文字欄位 | --BOUNDARY\r\nContent-Disposition: form-data; name="key"\r\n\r\nvalue\r\n |
| 檔案欄位 | --BOUNDARY\r\nContent-Disposition: form-data; name="file"; filename="a.jpg"\r\nContent-Type: image/jpeg\r\n\r\n[二進制資料]\r\n |
| 結束 | --BOUNDARY--\r\n |
File f = new File("/sdcard/photo.jpg");
Map params = new HashMap<>();
params.put("userId", "123");
FormFile ff = new FormFile(f.getName(), f, "photo", "image/jpeg");
ff.setOper("action=upload");
boolean ok = ff.post("https://192.168.1.100:8080/upload", params);
RequestBody.create(MultipartBody.FORM, ...) 一行搞定@Multipart @POST("upload") Call upload(@Part MultipartBody.Part file) BodyPublishers.ofByteArrays(parts)手工 Socket 組裝僅適合極度受限環境或教學理解協議細節,生產請用成熟庫。