socket模拟http请求,socket模拟http文件上传

后端接收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();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值