后端接收3个参数: file、remotePath、time
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file, String remotePath, String time) {
System.out.println(file);
System.out.println(remotePath);
System.out.println(time);
return "上传成功";
}
前端准备一个form表单
<form method="post" action="/upload" enctype="multipart/form-data">
<div>
<input type="file" name="file">
</div>
<div>
<input type="text" name="remotePath">
</div>
<div>
<input type="text" name="time">
</div>
<div>
<button>提交</button>
</div>
</form>
本地新建一个名为:myfile.txt的文本文件,txt文件内容:123456
发送一个form表单请求
请求后抓包如下:
POST http://localhost:8080/upload HTTP/1.1
Host: localhost:8080
Content-Length: 358
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQSFobk9lz8ATC9Ax
------WebKitFormBoundaryQSFobk9lz8ATC9Ax
Content-Disposition: form-data; name="file"; filename="myfile.txt"
123456
------WebKitFormBoundaryQSFobk9lz8ATC9Ax
Content-Disposition: form-data; name="remotePath"
123
------WebKitFormBoundaryQSFobk9lz8ATC9Ax
Content-Disposition: form-data; name="time"
456
------WebKitFormBoundaryQSFobk9lz8ATC9Ax--
然后 socket 模拟这个报文即可。
----WebKitFormBoundaryQSFobk9lz8ATC9Ax 这个字符串使用uuid模拟即可,保证唯一就行。
封装成http客户端工具
package com.study;
import java.io.*;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Socket模拟Http客户端
* @author
* @date 2023/3/31 18:05
*/
public class SocketHttpClient {
public static void main(String[] args) throws Exception {
String url = "http://localhost:8080/upload";
// 要上传的文件
Map<String, String> files = new HashMap<>();
// key:文件名 value:文件绝对路径
files.put("myfile.txt", "D:/myfile.txt");
Map<String, String> params = new HashMap<>();
// 请求参数:name=vaue
params.put("remotePath", "123");
params.put("time", "456");
String sendPost = sendPost(url, null, null);
System.out.println(sendPost);
}
/**
* 模拟POST请求
* @param url 请求路径
* @param files 上传的文件
* @param params 请求参数
* @author
* @date 2023/3/31 17:42
*/
public static String sendPost(String url, Map<String, String> files, Map<String, String> params) {
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
URI uri = new URI(url);
// 要连接的服务端IP地址和端口
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = 80;
}
// 与服务端建立连接
socket = new Socket(host, port);
socket.setSoTimeout(5000);//读超时
// 建立好连接后,从socket中获取输入流
inputStream = socket.getInputStream();
// 建立好连接后,从socket中获取输出流
outputStream = socket.getOutputStream();
//获取http模拟报文
byte[] httpSocket = getHttpSocket(url, files, params);
outputStream.write(httpSocket);
outputStream.flush();
//这里需要延时一段时间,等待文件上传完成,再发送终止符
Thread.sleep(1000);
socket.shutdownOutput();// 单向关闭输出流,发送流的终止符-1。
byte[] buf = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
//只有当客户端关闭它的输出流的时候,服务端才能取得结尾的-1
while ((len = inputStream.read(buf)) != -1) {
// 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
sb.append(new String(buf, 0, len, StandardCharsets.UTF_8));
}
String result = sb.toString();
if (result != null && result.indexOf("\r\n\r\n") != -1) {
return result.substring(result.indexOf("\r\n\r\n") + 4);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 拼接http报文
* @author
* @date 2023/3/31 17:47
*/
private static byte[] getHttpSocket(String url, Map<String, String> files, Map<String, String> params) throws Exception {
URI uri = new URI(url);
int port = uri.getPort();
if (port == -1) {
port = 80;
}
String boundary = UUID.randomUUID().toString().replace("-", "");
// 请求体
byte[] body = getBody(boundary, files, params);
String host = uri.getHost() + ":" + port;
ByteArrayOutputStream http = new ByteArrayOutputStream();
http.write(("POST " + url + " HTTP/1.1\r\n").getBytes(StandardCharsets.UTF_8));
http.write(("Content-Type: multipart/form-data; boundary=" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
http.write(("Host: " + host + "\r\n").getBytes(StandardCharsets.UTF_8));
http.write(("Content-Length: " + body.length + "\r\n").getBytes(StandardCharsets.UTF_8));
http.write("\r\n".getBytes(StandardCharsets.UTF_8));
http.write(body);
return http.toByteArray();
}
/**
* 拼接http请求体内容
* @author
* @date 2023/3/31 17:47
*/
private static byte[] getBody(String boundary, Map<String, String> files, Map<String, String> params) throws Exception {
// 定义存放 body 内容的缓冲区
ByteArrayOutputStream body = new ByteArrayOutputStream();
// 拼接文件部分
if (files != null) {
for (Map.Entry<String, String> entry : files.entrySet()) {
body.write(("--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
// name="file" 是表单name属性,注意要改成跟自己的一致。
body.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + entry.getKey() + "\"\r\n")
.getBytes(StandardCharsets.UTF_8));
body.write("\r\n".getBytes(StandardCharsets.UTF_8));
// 文件绝对路径
String path = entry.getValue();
byte[] buf = new byte[1024];
int len;
FileInputStream fis = null;
try {
// 将文件内容,写入缓冲区
fis = new FileInputStream(path);
while ((len = fis.read(buf)) != -1) {
body.write(buf, 0, len);
}
body.write("\r\n".getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw e;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 拼接普通表单参数部分
if (params != null) {
for (Map.Entry<String, String> entry : params.entrySet()) {
body.write(("--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
// 参数名
body.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n")
.getBytes(StandardCharsets.UTF_8));
body.write("\r\n".getBytes(StandardCharsets.UTF_8));
// 参数值
body.write(entry.getValue().getBytes(StandardCharsets.UTF_8));
body.write("\r\n".getBytes(StandardCharsets.UTF_8));
}
}
if (files != null || params != null) {
// 结束标记
body.write(("--" + boundary + "--").getBytes(StandardCharsets.UTF_8));
body.write("\r\n".getBytes(StandardCharsets.UTF_8));
}
return body.toByteArray();
}
}