EtherDream/freecdn

bundle新功能pack资源包格式和原理

lirko opened this issue · 11 comments

lirko commented
资源包格式很简单:

一个 JSON,记录每个文件的 HTTP 响应头(其中 content-length 字段必选,记录文件长度)

分隔符 \r (0x0D)

所有文件合并后的数据(按 JSON key 的顺序)
,"img/emoji/1f301.png":{"content-length":1089},"
如果打包的目录为 assets,那么文件 assets/1.gif 在资源包中的路径为 1.gif(无 / 前缀)。

看了一遍文档资源合并就是sw解析json对象
合并后的资源包json文件格式

{
"img/emoji/1f301.png":{"content-length":1089},"
…
1f648.png
1f649.png
…
二进制
…
}

还不如以下k:v格式简单粗暴来的好

{
"img/emoji/1f301.png":{"二进制"}
…
}

而且js css html这些文件是源代码,并没有压缩,传输方面也不合适吧

/emoji-icons/assets/
	bundle=/icons.fcpkg

/icons.fcpkg
	https://npm.elemecdn.com/free-host@0.0.0-v90rEGxJwu2TBCPV/index.js
	https://unpkg.com/free-host@0.0.0-v90rEGxJwu2TBCPV/index.js
	https://cdn.jsdelivr.net/npm/free-host@0.0.0-v90rEGxJwu2TBCPV/index.js
	hash=1048576;onDac6KIBKj+JIjn026vj8rI6SqPI1fCGcTRZUExqJk=,sXv/uBmv4WzCCIjfaCCXccQRkwG6i3/GodF8l9El6SY=
	size=1090870

当然文档没说.fcpkg文件支不支持br=on
如果支持br=on倒是合并什么文件都没关系

但就这个json格式的设计感觉原本简单的k:v可以胜任为何要复杂化

{
k:文件长度
…
文件名
…
文件二进制
…
}

造成资源计算浪费?

另外合并功能是
首次下载完json后一次性全部提取出来缓存浏览器,让清单原文件每次有请求时就直接返回文件?

还是

每次清单原文件请求时每次都解析json文件提取其中的文件返回?

如果每次取文件都要解析一次json
假如json文件5M
为了取一次20k的html解析一次5m的json?
为了取一次50k的css又解析一次5m的json?
为了取一次10k的js又解析一次5m的json?

该功能没发布前我还以为会采用类似JSZip tar.gz等通用的压缩功能编译成WebAssembly,这样就可以使用阿里淘宝/腾讯云/华为云等厂商提供的npm镜像加速服务
https://registry.npmmirror.com/
https://mirrors.cloud.tencent.com/npm/
https://mirrors.huaweicloud.com/repository/npm/

除了众多国内npm镜像仓库还有国内docker仓库镜像可以利用上

支持 br 参数。不过还是推荐在 HTTP 层传输压缩,这样效率高一些。比如 unpkg.com 返回的本身就有 br 压缩。(以上述资源包为例 https://unpkg.com/free-host@0.0.0-v90rEGxJwu2TBCPV/index.js)

用 JSON 主要是解析简单一些,还可以配自定义返回头。额外的冗余压缩之后也不是很大。

请求是有缓存的,而且有两层缓存。资源包本身会缓存在 CacheStorage 里,解析之后会把小文件缓存在内存里,之后访问同个资源包的文件直接从内存里读取。

之前也想用 tar 格式,不过研究下了发现 tar 的冗余很大,而且很多字段都是无用的,文件属性权限之类的。

阿里淘宝/腾讯云/华为云等厂商提供的npm镜像加速服务不支持 cors,前端读取不了数据。

当然这个参数还有很多功能没加上,这几天再更新。

lirko commented

如果直接http层压缩,对于伪装附加到图片后的方案就无效,这样能选择的cdn节点就很少

这种json格式具体有什么含义?

{
k:文件长度
…
文件名
…
文件二进制
…
}

为何不直接使用

{
"img/emoji/1f301.png":{"二进制"}
…
}

起码节省了记录文件大小和节省计算二进制位置的资源,k:v格式起码其他语言对接生成json时方便

"img/emoji/1f301.png":{"二进制"} 用字符串存二进制就要编码了,一般用 base64,体积会大很多。

所以只在文件开头存一个 json 字符串,后面的文件都二进制合并一起。

lirko commented

发布后也在想json不能直接存二进制只能编码后存,不然二进制不会写在json外层。

每个功能很实用,但建议功能文档尽量多的适用场景,让使用者有个大致方案利用哪个功能来开发什么样的项目方案

比如这个合并功能场景
如果用于.html > .br,不理透解析资源包后,文件缓存到浏览器是缓存压缩后的br文件还是压缩前的html文件。
以上看演示的json已经得知是缓存html原文件而不是br,所以如果要使用html合并就要优先考虑各个浏览器支持容量问题。

再比如hash校验功能,一直以为获取远程文件后第一时间是校验返回的文件hash值,不一致就抛弃进行下一个地址请求,但实际freecdn前端脚本却是先处理pos/xor/等等操作后最后才校验hash,这样最后校验hash不一致前面的pos/xor等计算可以说是完全浪费了,也幸好看到文档:

"参数优先级"
参数并不以清单中的顺序生效,而是按 内置的优先级。
例如 hash 校验的是最终呈现的数据,而不是裁剪、拼接、解压前的数据。

如果不仔细看到此说明,用php操作html原文件压缩成br后伪装到图片后尾,上传cdn节点,计算文件hash保存清单。

但很遗憾根据文档的 参数优先级 freecdn cli第一时间记录html 的hash值,再进行br压缩,合并图片后尾,上传cdn节点,保存清单。

文档我再完善下,目前还比较简单。

hash 是因为同个文件可能有不同的封装方式。比如 npm 上直接存原文件就可以,图床需要加一个图片头,不同的图床图片头可能不一样,所以 url 层面的 hash 可能不一样。hash 错误是小概率事件,所以解压、裁剪等浪费也还好。

这个问题我还在考虑怎么改进 cli 的搜索方式。

我打算加一个自包含的文件格式,文件内容用私钥加密,前端用公钥解密,这样就免去 hash 校验了(虽然安全性没 hash 高,但也够用),而且自带加密。

起始位置可以用固定的标志代替,图片里找到这个标记,就自动从它开始提取,免去 pos 参数。

再加上一些文件头,标记内容是不是 br 压缩,要加哪些自定义头等等。

这样清单里只需配 url 就可以,文件对应哪些 url,什么参数也不用填。

lirko commented

无需hash校验,如果文件被篡改,前端公钥无法解密,代表文件已经被篡改,这就已经算是在校验文件完整性,无法解密直接请求下个备用地址

淘宝图片cdn节点支持cors,我把 数据附加在图片后面上传,会删除了数据部分只保留图片部分,如果上传 数据转成canvas压缩的图片能完整保留,类似的canvas压缩数据,其实也充当了加密,减少了体积,但存在解码性能。

我拿淘宝图片说事因为它是一个公开使用的图片服务,其他的百度/腾讯/头条/bili等图片接口可以说是漏洞接口,更无法保证服务。
很多图床节点存在的情况

不一定保存图片外的数据
对图片压缩剪裁甚至格式转换处理

你提出的新文件格式,请考虑把数据压缩转换成真图片像素而不是附加到图片尾后来合并,canvas性能问题,可以走wasm解码图片。
至于其他的文件头,br压缩,自定义头等改不改方案问题不大,最终还是编码成图片像素,前端只要能成功解码图片还原数据,原始文件有什么br等逻辑只是标记问题,只不过是把标记直接放到文件里面而不是清单里面

放在像素里之前做过一个演示:https://github.com/EtherDream/web2img 不过比较简陋,所有文件只能打包成一个图片,纯粹演示而已。

之后打算加一个从图片提取内容的参数,而且高版本浏览器本身就原生支持 zlib 解码,性能应该不错。

lirko commented

所有文件只能打包成一个图片

并不是缺点而是优点,能上传js css html的节点必然能上传图片,但能够上传图片的节点就并非可以上传js css html等文件了,当然选择很广泛的图片格式来做数据存储。

另外说到canvas压缩字符串成二进制图片
可否把原本就是二进制(exe rar 甚至图片)压缩二进制图片,这也可以说是间接给这些二进制进行压缩一次了,对方即使能找出备用地址的图片地址保存,也很难对这些二进制进行解码还原。

例如图片网站使用这种方案后,对方能获取到图片的请求地址,但响应却是一个canvas编码压缩后的非正常展示的像素图片

如:网页展示的是一张美女图片,捕捉到请求地址后打开却是一张大叔图片,但他确定美女图片肯定是在大叔图片这个请求转换来的,但对方不知道解码规则,最后无法从大叔图片还原成美女图片而选择放弃

图片本身就会压缩,不过大部分服务端会重新压缩一次,压缩率远没有 br 高。压缩后都是杂乱的点,也看不出什么内容,要显示另一种内容的话得加入大量数据,图片体积得翻一翻了。可以试一试那个 web2img 看图片效果。

lirko commented

最终展示杂乱的点就相当于一次 加密
canvas如果可以压缩二进制,也相当于对二进制文件加密一次
比如.html > .br > .png
.css > .br > .png
压缩成br后变成了二进制>最后变成图片
不是非要canvas实现,只是容易理解,毕竟有性能问题,有其他方案代替canvas当然更好,例如你说的zlib 解码