摘要

  • RFC 1867 与 RFC 2388 都把一个字段中的多个文件表示为嵌在 multipart/form-data 内部的 multipart/mixed 部分。
  • RFC 7578 记载没有已知发送方实现过这种发送方式,因而改为每个文件一个外层部分、共享字段名,并保留接收端兼容旧嵌套形式的要求。

文件上传先要有一张网络“包装图”

在浏览器表单能够用通用方式提交本地文件内容之前,服务端往往需要定制客户端。1995 年的 RFC 1867 提出 HTML 的 INPUT TYPE=FILE 控件,以及一种同时承载普通字段和文件字节的 MIME 表示。它的状态是 Experimental,并非已经定稿的互联网标准;它还考虑了无法上传的旧用户代理应如何过渡。

这种格式分了两层。外面的 multipart/form-data 部分标出表单字段名;若用户在同一字段选择多个文件,里面再套一段 multipart/mixed,逐个容纳文件。每层看起来各司其职:外层关联控件,内层收纳集合。

1998 年的 RFC 2388 获得 Standards Track 状态,并把 multipart/form-data 定义成独立于 HTML、HTTP 的表单值表示,其他应用也可以采用。但当单个字段装有多个文件时,它仍沿用内嵌 multipart/mixed。实验性提案成为可复用的规格,原有的多文件结构也随之进入标准文本。

后来的修订没有找到这种发送端

2015 年,RFC 7578 取代 RFC 2388,并直接记录了文本与实现之间的落差:对于创建者,它不再推荐嵌套的 multipart/mixed;对于接收者,也不再要求必须实现这种格式,因为作者不知道有哪种发送方实现过它。为匹配他们能够确认的实现,每个文件改用独立的外层表单数据部分;同一控件的多个部分重复同一个 name 参数。

分组关系因此换了位置。旧设计是在一个命名部分里面再放文件列表;新设计则把多个同级部分排在外层,通过共同的字段名表达它们属于一个表单项。RFC 7578 没把旧结构宣布为无效输入,还建议通用接收程序继续兼容。发送规则变了,读取旧数据的兼容路径仍要保留。

“没有已知发送方”说的是修订者掌握的记录,不能证明历史上从未有程序发出过嵌套形式。规范没有给出浏览器名单、部署比例、切换日期或故障率。它能证明的范围更窄,却很重要:后续标准之所以改写多文件表示,是因为作者找不到会发送前一版所描述结构的实现。

重复字段名不是同一个值

RFC 2388 还没有规定表单字段顺序与返回部分顺序之间的关系,也没定义重复字段名如何处理。RFC 7578 后来要求在表单有明确顺序时尽量保持顺序,不许中间系统重排结果,也不许把同名部分合并。于是重复字段名可以表示一项的多个值,而不是键冲突。

这并不是把 MIME 的结构全部删掉。MIME 仍负责分隔各个部分;而且有些表单本来就不存在自然顺序。修正聚焦于一个特定场景:多文件控件由多个外层并列部分组成,再以同名字段关联起来。整个请求、表单预设的顺序、每个部分的字节,以及接收应用如何解释,仍是不同层次。

运行中的代码是一条证据

Heng Lu 的 Running-Code Primacy 文章强调,互联网的实际状态不能只从规范文本推断。RFC 2388 的后续修订给出了一个范围有限而清楚的例子:RFC 7578 没有因为旧规则已经写入标准就照单保留。作者依据能够识别的发送端行为改变多文件格式,同时要求接收端为旧结构留出兼容能力。

记录没有说是谁推动了修订、涉及多少软件,或什么运维成本促成变化。这也不表示实现者总是正确,或标准应当照抄任意软件习惯。较克制的教训是:先规定线上格式,再查找真正会发送它的系统;当规则改动时,也要考虑如何读取仍可能存在的旧数据。

来源