繁体   English   中英

解析 `multipart/form-data` http 请求是如何工作的?

[英]How does parsing the `multipart/form-data` http request work?

介绍

目前我正在研究用 Java 编写的简单文件服务器,它使用 sockets 进行通信。 在这个项目中,我对 http 请求的格式感兴趣,并希望在我的项目中复制它。 我想在低级 API 上执行此操作,仅使用 sockets 来了解这一切是如何在幕后工作的。

tldr;

问题很简单,它位于帖子的最后一部分。 其他一切都是解释和我对问题的理解。

先决条件

在下面的示例中,我将使用带有 sockets 的简化代码来展示我是如何理解事物的。 我还假设存在以下变量:

Socket socket = server.accept();
DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream output = new DataOutputStream(socket.getOutputStream());

解析 Http 请求

好的,所以解析示例application/x-www-form-urlencoded http 请求(或类似请求)对我来说似乎很容易理解,但如果我错了,请纠正我。 有示例请求:

POST / HTTP/1.1
Content-Length: 64
Content-Type: application/x-www-form-urlencoded

name=John%20User&request=Send%20me%20one%20of%20your%20catalogue

示例服务器可以用这种方式解析这个请求:

// read start-line of request
String startLine = input.readline();
...

// read all headers till you encounter empty line
String header;
while (!(header = input.readLine()).equals("")) {
  ...
}

// read body
int len = <Content-Length header value>;
byte[] body = new byte[len];
input.read(body, 0, len);
...

解析multipart/form-data http 请求

这是我的主要问题。 让我们有一个示例性的多部分请求。

POST / HTTP/1.1
Content-Type: multipart/form-data; boundary=boundary
Content-Length: 465

--boundary
Content-Disposition: form-data; name="name"

John
--boundary
Content-Disposition: form-data; name="avatar"; filename="avatar.jpg"
Content-Type: image/jpeg

<some binary data>
--boundary--

我不确定如何解析这样的请求。 Start-line 和 headers 的解析方式与前面的示例类似,但如何处理 body,尤其是当其中有二进制数据时。 我有一些想法,但认为它们是错误的/不够的。

我的尝试

我的尝试是将 body 读取为字符串。 稍后可以使用边界值将该主体划分为多个部分,然后服务器可以处理这些分离的部分(例如提取标头,使用值做一些事情等等)。 它可能看起来像这样:

int len = <Content-Length header value>;
byte[] byteBody = new byte[len];
input.read(byteBody, 0, len);

String boundary = <extracted from header>;
String body = new String(byteBody);
String bodyParts = body.split(boundary)
...

然后我遇到了一个问题,它不适用于二进制文件。 byte[]转换为String ,然后再转换为byte[] (在服务器上写入文件)不能用于文件。 这是因为默认编码是 ASCII 并且它不支持负值。 我做了一个小测试,这是结果。

byte[] arr1 = new byte[] { -1, -2, -3 };
String str1 = new String(arr1);
byte[] arr2 = str1.getBytes();

// arr1 = [-1, -2, -3]
// arr2 = [-17, -65, -67, -17, -65, -67, -17, -65, -67]

在获得这些知识后,我寻找解决这个问题的方法。 我认为base64编码可以解决我的问题,但它对我来说似乎是一种解决方法,并且有其缺点:

  • 使文件大小变大,
  • 需要服务器和客户端实现base64的编码/解码。

我还找到了许多示例,并做了一个简单的 node.js 服务器来证明这一点,在multipart/form-data请求正文文件的情况下,绝对可以以二进制格式发送,而不是base64之一。

混乱

我现在有点困惑。 我不知道如何解析multipart/form-data请求正文,这样我就不会将其转换为字符串,但仍然可以使用boundary值将其分成单独的部分。 我考虑过逐字节读取此正文并以某种方式检测边界,但这在我看来并不是一个好的或有效的方法。

我真的很好奇完成该任务的正确方法是什么,以及解析这种类型的请求主体的标准是什么。

正文格式是这样的:

  1. --其次是边界
  2. 任意数量的标头,类似于根 HTTP 请求的标头
  3. 一个空行,类似于根 HTTP 请求的 headers 和 root body 之间的行
  4. 分体
  5. 其他部分重复1-4
  6. --后跟边界--

我不使用拆分,而是逐行解析正文; 如果遇到边界,则完成前一部分(第一个部分无需执行)。 如果你遇到身体结束,你就完成了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM