抓包文件上传

1,567 阅读2分钟

一、前言

了解文件上传原理,HTTP 底层处理,搜索关键字:RFC1867

HTTP 传输的是二进制数据。

HTTP 请求可分为两部分:

\r\n\r\b 作为 headerbody 分割

  1. header 部分:请求方法、URL、协议版本、content-length
  2. body 部分:即内容,payload



读取 HTTP body 部分,流程大致如下:

  1. 根据 boundary 分析出分隔符特征
  2. 根据实际分隔符分段获取 body 内容
  3. 遍历分段内容
  4. 根据 Content-Disposition 特征获取其中值
  5. 根据值中 Content-type 区分二进制流还是表单数据的 kv
  6. 获取文件原始文件名
  7. 两个连续 \r\n 起始,按照二进制流读取上传文件流信息

二、实战

(1)服务器代码

这里使用 SpringBoot 进行开发。

@RestController
@RequestMapping("/file")
public class FileController {
    
    @PostMapping
    public FileInfo upload(MultipartFile file) throws IOException, InterruptedException {
        // 上传文件到本地 
        try (BufferedInputStream in = new BufferedInputStream(file.getInputStream());
             FileOutputStream fileOutputStream = new FileOutputStream(file.getOriginalFilename())) {
​
            byte dataBuffer[] = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
                fileOutputStream.write(dataBuffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
​
        return null;
    }
}

(2)curl 请求

发送上传文件请求:curl localhost:8080/file -F "file=@/home/donald/Pictures/2019-06-1010:00.png" -v

详细如下:

donald@donald-pro:~$ curl localhost:8080/file -F "file=@/home/donald/Pictures/2019-06-1010:00.png" -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /file HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 282942
> Content-Type: multipart/form-data; boundary=------------------------6e0a716fee48c6d0
> Expect: 100-continue
> 
< HTTP/1.1 100 
< HTTP/1.1 200 
< Content-Length: 0
< Date: Fri, 23 Jul 2021 04:16:01 GMT
< 
* Connection #0 to host localhost left intact

(3)wireshark 抓包

完整请求如下:

2021-07-2315-35-05.png

由这图,可以得出:

  1. 上传大文件前使用 100 Continue:会先发送 http 100 的请求,header 中携带 Expect: 100-continue
  2. 发送完 http 100 之后,就开始传输数据了
  3. 传输数据完成之后,客户端会发送 POST /file HTTP/1.1 (PNG)
  4. 再之后,服务端响应客户端的 POST,那么说明上传文件完成了,就可以 4次挥手了。

选中一个报文,右键 Follow -> TCP stream,如下:

2021-07-2315-22-53.png

完整数据流,概括如下:

POST /file HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.58.0
Accept: */*
Content-Length: 282942
Content-Type: multipart/form-data; boundary=------------------------6e0a716fee48c6d0
Expect: 100-continue

HTTP/1.1 100 

--------------------------6e0a716fee48c6d0
Content-Disposition: form-data; name="file"; filename="2019-06-1010:00.png"
Content-Type: image/png

.PNG
.
...

--------------------------6e0a716fee48c6d0--
HTTP/1.1 200 
Content-Length: 0
Date: Fri, 23 Jul 2021 04:16:01 GMT