聊聊阿里云 OSS 的转义设计问题
今天同事向我反馈,当我们的静态资源有中文名混杂 + 和空格 时,得到的链接并不能打开,返回的是 404.
举个例子,假定我们的资源为:
http://example-resource.oss-cn-shanghai.aliyuncs.com/23232323/中文 + 文件-file.ec296f20-d67b-11e8-a623-7b28809dc3c9.js
那么在浏览器中访问的文件名应该被转义为:
%E4%B8%AD%E6%96%87%20+%20%E6%96%87%E4%BB%B6-file.ec296f20-d67b-11e8-a623-7b28809dc3c9.js
注意,在这里,空格被转义为 %20
,而 +
没有进行任何处理。
同时,JavaScript 中的 encodeURI
和 new URL()
都是符合这一规则的。
但是在阿里云 OSS 中,你可以在 404 页面看到他们后端请求的文件:
此时的一个问题:到底是存错了还是取错了——其实在这里我们基本可以看出取的是有问题的,因为对于这个 path 的语义应该是+
,却被错误的处理成了空格,不过考虑到严谨性,还是确认了一下存储的 Object,确定是取的问题。
接下来的问题是:
- SDK 给我们返回的阿里云链接是可以直接访问的,而我们自定义拼接的链接是不可访问的,他们做了什么特殊处理
- 到底是哪里出了问题
因此,不得已的只能把 Node SDK 源码看了一遍……终于发现了他们的骚操作:
https://github.com/ali-sdk/ali-oss/blob/master/lib/client.js#L366
在 encode 之后人工 2B,谁都比不过——
第二个问题,到底是谁的错?
于是正好上 so 搜了一下,发现了一波标准:https://stackoverflow.com/questions/2678551/when-to-encode-space-to-plus-or-20
+
means a space only inapplication/x-www-form-urlencoded
content, such as the query part of a URL
Now, the HTML 2.0 Specification (RFC1866) explicitly said, in section 8.2.2, that the Query part of a GET request's URL string should be encoded as ·application/x-www-form-urlencoded·. This, in theory, suggests that it's legal to use a '+' in the URL in the query string (after the '?').
也就是说,只有 application/x-www-form-urlencoded
和 querystring
中的空格 = +
,+
= %2B
,其他情况下 +
不应该被特殊处理,很显然阿里云 OSS 的实现是错误的。
得到问题的答案之后,基本定位到是 OSS 后端的转义问题,但是去怼 OSS 的时候,他们用「这是 Feature」、「为了复杂规则更好的兼容」、「设计就是这样」、「用户最好自己 encode」来答复,这里有涉及到内容存储的另一个结构了:
对于内容存储而言,会将路径也作为文件名的一部分,即上例中的文件名是:
23232323/中文 + 文件-file.ec296f20-d67b-11e8-a623-7b28809dc3c9.js
那么我们处理的时候,如果使用 encodeURI
,则不会转义 +
,如果使用 encodeURIComponent
,则会把 /
一起转义,如果在保存前转义,就无法保留正确的文件名——非常矛盾,这大概也就是为什么 SDK 用了 + => 2B
的黑魔法吧。
不过我也有个大胆的猜测,因为阿里云这段代码非常年长而稳定了,而我们这样的考据用户其实并不多,凑合用黑魔法也能用,所以所谓的「Feature」,就是——改了万一挂了我可背不起锅,即使是加一个兼容 Feature,也是有可能挂的,所以最好的方法还是让用户继续用黑魔法,但是建议大家以后在做类似的实现的时候不要理所当然的以为 url 中的 +
必然等于空格,在标准中是有明确规定的。
小插曲:最开始以为是 JavaScript encode 库有问题,怎么都不能和阿里 encode 的结果一致,还骂了一声 JavaScript 辣鸡,如果是 PHP 估计就没问题(喂。定位了一天并且和阿里云撕逼之后只能说——阿里云辣鸡(喂。
植入部分
如果您觉得文章不错,可以通过赞助支持我。
如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。
膜天总,阿里云辣鸡
哈哈 也遇到这个坑了,还以为没有进行 url encode 才会的,发现 encode 了也会这样,只能特殊处理了