起因
最近隔壁的有个同事突然发问,我们有个商店页面可以用来上传 apk ,他们部门内部其实是有个 npkg 平台用来上传 apk 测试、生产包的。所以提出了一个问题:
有没有办法可以实现点击上传时候打开 npkg 平台选择内容后直接把 apk 包传到商店页面
让商店页面来适配 npkg 平台是 不现实的,因为这块的业务 是对外的,即使是一方的应用也不可能以这个理由提供支持,但是他们提出可以考虑让运营安装 chrome 插件来 绕过一些安全策略,这个想法 非常有意思
,我认为理论上 应该是可行的。
于是尝试写个 demo 试试。
实现
抽象行为
首先我们需要抽象一下行为,这个里面的步骤大致可以分为如下这几个步骤:
- 识别 商店上传input,并在上面绘制一个触发按钮
- 点击触发 按钮,弹出 npkg 页面
- 在 npkg 中点击列表内容
- 将文件添加到 input
我们分步骤来看,第一步是最容易的,无非是找一下 input 特征即可。
第二步应该是不难做的,弹出 npkg 页面可以用 window.open 来实现,然后基于窗口通讯即可,当然也可以直接使用 iframe 嵌入,然后利用 postMessage 实现,也都不难。
第三步点击列表内容需要他们自己适配一下
比较有问题的是第四步,其中有两个比较主要的问题。
- pkg 包不会小,由于某些原因,这些游戏包的大小可能会很大,譬如可能
达到 2G 以上
- 怎么样给 Input 设置 file ,并触发响应
过大的 pkg 包
由于是游戏 app,所以大小不可控,有的会到 2G 以上,很重要的一点是 2G 是一个分水岭,因为 chrome 对 blob 大小的限制是有规定的,具体可以前往 源码 查看:
这里只截取了注释:一旦 blob 超过 2G 那么就无法正确的分配内存了。
不过我们可以考虑不把 blob 放置到内存里面,因为很简单,基于 file 的 blob 的最大可以用 disk 的 10% 这个是一个非常巨大的数字。
写一个 demo
下面我们需要写一个 demo 实现一下。
图快这里用 vscode 的 live-server插件
随后使用如下命令创建一个超大的文件:
设置下载
设置下载很简单, 首先我们要获取元素的大小,然后预分配:
循环下载
由于文件太大,所以不能一次性写入,需要分块写入,且不能使用同一个 fetch 请求, 因为同一个 fetch 请求也会载入到内存,突破 chrome tab 占用内存的上限
触发change 事件
虽然 FileList 是不能直接修改的1,因为本身 FileList 是一个 attempt to create an unmodifiable list
2 的行为,所以不能修改。
而 FileList 没有提供对外的 new 方法,所以也同样不能创建。
但是我们有 DataTransfer,他 items 是一个 FileList 所以可以用它来生成 input 需要的 files 字段,随后利用自定义事件触发 event 即可。
完整测试代码
完整代码使用 image 来模拟,用来模拟成功效果: