开始
- 最近代码重构遇到了一个问题,需要把
OSS
上的一批图片打包下载 - 旧服务器的硬盘是直接挂载
OSS
,所以直接调的Linux
系统命令复制打包,所以速度比较快。新服务器重构代码行不通,这样做也不好 - 查阅
OSS
并没有提供相关API
(七牛支持)
解决方法:
- PASS掉
PHP
后台fetch
图片到内存然后进行打包输出给前端(图片有ai
,psd
内存撑不住) - PASS掉
PHP
后台fetch
图片到服务器硬盘,然后进行打包输出一个临时链接给前端(同上,文件太大,导致fetch
时间和打包的时间太久) - JS 尝试通过前端请求后端,后端返回文件信息,然后前端异步请求所有文件,最后在前端打包下载
前提
想法很好,并且已经有人这样做了,我们只用考虑按照别人的做法坐下去
前端打包有两个前提:
- 跨域问题,需要运维在
OSS
或者下载的资源服务器设置允许跨域 CDN
问题,让运维配置一个新域名不要走CDN
(如没使用CDN
可忽略)
步骤
- 使用https://github.com/Stuk/jszip这个库打包文件
- 使用https://github.com/Stuk/jszip-utils获取远程数据(直接通过
AJAX
获取,需要转换格式) - 直接下载这两个库,然后把
dist/
下的文件放入到项目,也可以用github
提供的方式引入 - 部分逻辑代码如下
// 引入文件
<script type="text/javascript" src="/xxx/jszip.min.js"></script>
<script type="text/javascript" src="/xxx/jszip-utils.min.js"></script>
<!--[if IE]>
<script type="text/javascript" src="/xxx/jszip-utils-ie.min.js"></script>
<![endif]-->
<script>
let total = 0;
let progress = 0;
// 可通过按钮或者其它事件触发
$.get('/urls', function(res) {
// 假设 res.data 是后端返回的一组远程数据对象
// {url: 'x.jpg', filename: 'xx.jpg', path: 'xxxx'}
total = res.data.length;
if (total === 0) {
console.error('图集无图片可下载');
return;
}
let zip = new JSZip();
// map 存储 folder 对象
let folderMap = {};
// 遍历所有后端返回的 urls
res.data.map(function (data) {
// 获取远程资源数据
JSZipUtils.getBinaryContent(
data.url,
function (err, binData) {
++ progress;
if (err && progress < total) {
console.log(err);
return;
}
// 第一次实例化 folder 对象
if (! folderMap[data.path]) {
folderMap[data.path] = zip.folder(data.path);
}
let pf = folderMap[data.path];
// 第三个参数必须设置,否则下载的文件将产生错误
pf.file(data.filename, binData, {binary: true});
if (progress === total) {
console.log('请稍等,数据打包中');
zip.generateAsync({type:"blob"}).then(function(content) {
// 下载文件
const link = document.createElement('a');
link.href = window.URL.createObjectURL(content);
link.download = 'data.zip';
link.click();
document.body.removeChild(link);
});
}
}
);
})
})
</script>
完成之后,发现效果还不错。速度也比后端的来的块。毕竟不经过后端。而且可以多个请求异步处理。