一、前言
了解文件上传原理,HTTP
底层处理,搜索关键字:RFC1867
。
HTTP
传输的是二进制数据。
HTTP
请求可分为两部分:
用
\r\n\r\b
作为header
和body
分割
header
部分:请求方法、URL
、协议版本、content-length
等body
部分:即内容,payload
读取 HTTP body
部分,流程大致如下:
- 根据
boundary
分析出分隔符特征 - 根据实际分隔符分段获取
body
内容 - 遍历分段内容
- 根据
Content-Disposition
特征获取其中值 - 根据值中
Content-type
区分二进制流还是表单数据的kv
- 获取文件原始文件名
- 两个连续
\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
抓包
完整请求如下:
由这图,可以得出:
- 上传大文件前使用
100 Continue
:会先发送http 100
的请求,header
中携带Expect: 100-continue
- 发送完
http 100
之后,就开始传输数据了 - 传输数据完成之后,客户端会发送
POST /file HTTP/1.1 (PNG)
- 再之后,服务端响应客户端的
POST
,那么说明上传文件完成了,就可以 4次挥手了。
选中一个报文,右键 Follow
-> TCP stream
,如下:
完整数据流,概括如下:
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