使用jszip压缩文件夹
发表于:2024-05-14 |

前言

大家好,好久没更新了,这主要是因为我最近懈怠了,没咋学习,其次这段时间公司里面也挺忙的,经常加班,导致我回家只想着打打游戏,五一假期我在练车,也没学习,最近稍微好点,有开始学习,不过是在看英语,我这个人就是学习兴趣一阵一阵的,啥都想学一点,啥也不是很精,有机会得好好做个规划,废话不多说,今天给大家介绍一下jszip压缩文件夹的方式,这是我公司的需求啊,之前只是让我把文件展开上传就行了,现在居然要保留原先的目录结构,真的需求越来越离谱,能咋办,整呗

前置代码

基础架子我还是用之前我写过的jszip压缩的代码。
参考地址:https://myblog-5g89ixpbbf1fbfad-1316695488.ap-shanghai.app.tcloudbase.com/2024/04/09/jszip-study/

纠错

我之前写的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 得到文件大小
const getFileSize = (size) => {
let initNum = 0;
const unit = ["Bytes", "KB", "MB", "GB", "TB"];
const returnValFunc = () => {
return size.toFixed(2) + " " + unit[initNum];
};
if (size <= 1000) {
return returnValFunc();
}
// 不需要
// size /= 1000;
while (size > 1024) {
size /= 1024;
initNum++;
}
return returnValFunc();
};

这里面的size/=1000这个代码是不需要的,加了反而导致进制报错

列表展示修改

首先就是我们列表展示要进行修改,不能将文件直接展开了,需要将文件夹的名称作为一行进行push
我们先来看一下逻辑,就是handleFileFolderAdd函数的表层逻辑应该是变成这样

1
2
3
4
5
6
7
8
9
10
// 如果是文件夹类型
if(fileEntry.isDirectory){
// 当前需要添加的文件夹
let curNeedAddFolder={
name:fileEntry.name,
size:0,
time:new Date().toLocaleString(),
}
this.fileList.push(curNeedAddFolder)
}

但是这样也会有一个问题,size没了,所以我们还是需要之前的展开逻辑,将size进行累加计算,因此我将代码改成了这样,这里通过闭包和async await以及promise来实现了异步递归循环函数的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 获取文件夹下文件的entries
const readEntriesAsync = (reader) =>
new Promise((resolve) => {
reader.readEntries((results) => {
const cacheRes = results.map((item) => ({
entry: item,
}));
resolve(cacheRes);
});
});

// 将文件entries变成file
const fileEntriesToFiles = (entrie) =>
new Promise((resolve)=>{
entrie.file((file)=>{
resolve(file);
})
})

// 处理拖拽文件(夹)添加
const handleFileFolderAdd = (items) => {
for (let j = 0; j < items.length; j++) {
const item = items[j];
const fileEntry = item.webkitGetAsEntry();
// 当次文件夹文件大小
let totalSize = 0;
// 如果是文件夹类型
if (fileEntry.isDirectory) {
// 文件夹类型,递归处理
const reader = fileEntry.createReader();
// 当前需要添加的文件夹
let curNeedAddFolder = {
name: fileEntry.name,
size: 0,
time: new Date().toLocaleString(),
};
const readEntries = async (reader) => {
return new Promise(async (resolve, reject) => {
const readEntriesRecursive = async (reader) => {
try {
const results = await readEntriesAsync(reader);
for (let i = 0; i < results.length; i++) {
const curItem = results[i];
const curFileEntry = curItem.entry;
if (curFileEntry.isFile) {
const file=await fileEntriesToFiles(curFileEntry)
totalSize += file.size;
} else {
await readEntries(curFileEntry.createReader());
}
}
console.log(totalSize, "totalSize")
resolve(totalSize); // 当循环递归完成后,解析 Promise,并传递 totalSize
} catch (e) {
console.error(e, "e");
reject(e);
}
};
await readEntriesRecursive(reader);
});
};
readEntries(reader).then((totalSize) => {
fileList.value.push({
...curNeedAddFolder,
size: totalSize,
});
});
}
// 如果是文件类型
else if (fileEntry.isFile) {
fileEntry.file((file) => {
handleFileAdd([file]);
});
}
}
};

此时效果如下

根目录fileEntry处理

这样,我们页面上的显示的确没问题了,但是这个文件夹,里面没有关联一点和文件内容相关的东西,这样肯定是有问题的。接下来我们将根目录的fileEntry也给push到fileList当中,以便后续的zip操作。

将返回不用entrie包一层

当时我这么做是因为之前写的时候公司有另外的需求,有参数处理,这里,咱们按照简单的来

1
2
3
4
5
6
7
// 获取文件夹下文件的entries
const readEntriesAsync = (reader) =>
new Promise((resolve) => {
reader.readEntries((results) => {
resolve(results);
});
});

把第一次entries进行push

这里我加了个判断,如果是第一次处理文件夹目录的,将它的entries赋值给rootEntries,然后在最后push的时候给加到fileList里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 处理拖拽文件(夹)添加
const handleFileFolderAdd = (items) => {
for (let j = 0; j < items.length; j++) {
const item = items[j];
const fileEntry = item.webkitGetAsEntry();
// 当次文件夹文件大小
let totalSize = 0;
// 如果是文件夹类型
if (fileEntry.isDirectory) {
// 文件夹类型,递归处理
const reader = fileEntry.createReader();
// 当前需要添加的文件夹
let curNeedAddFolder = {
name: fileEntry.name,
size: 0,
time: new Date().toLocaleString(),
};
// 是否是根目录
let isRoot = true;
// 根目录下初级结构的entries
let rootEntries = [];
const readEntries = async (reader) => {
return new Promise(async (resolve, reject) => {
const readEntriesRecursive = async (reader) => {
try {
const results = await readEntriesAsync(reader);
if (isRoot) {
rootEntries = results;
isRoot = false;
}
for (let i = 0; i < results.length; i++) {
const curFileEntry = results[i];
if (curFileEntry.isFile) {
const file = await fileEntriesToFiles(curFileEntry);
totalSize += file.size;
} else {
await readEntries(curFileEntry.createReader());
}
}
console.log(totalSize, "totalSize");
resolve(totalSize); // 当循环递归完成后,解析 Promise,并传递 totalSize
} catch (e) {
console.error(e, "e");
reject(e);
}
};
await readEntriesRecursive(reader);
});
};
readEntries(reader).then((totalSize) => {
console.log(rootEntries, "rootEntries");
fileList.value.push({
...curNeedAddFolder,
size: totalSize,
fileEntry: rootEntries,
});
});
}
// 如果是文件类型
else if (fileEntry.isFile) {
fileEntry.file((file) => {
handleFileAdd([file]);
});
}
}
};

此时我们看一下,我们将entry给加进去了
效果图

压缩列表处理

这里我用到了jszip打包文件夹的方法,zip.folder这里需要传个文件名进去,然后后续属于这个文件夹的内容我们只需要将文件塞进去就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// 压缩包的实例
const zip = ref(null);

// 文件夹压缩
const addFolderToZip = async (folderPath, folder) => {
return new Promise((resolve, reject) => {
const handleFolderToZip = async (folderPath, folder) => {
try {
// 获取文件夹中的文件和子文件夹列表
for (let i = 0; i < folder.length; i++) {
const item = folder[i];
if (item.isFile) {
// 如果是文件,则将文件添加到 ZIP
const file = await fileEntriesToFiles(item);
zip.value.file(folderPath + "/" + file.name, file);
} else if (item.isDirectory) {
// 如果是子文件夹,则递归调用该函数
zip.value.folder(folderPath + "/" + item.name);
// 对文件夹内容进行解析
const results = await readEntriesAsync(item.createReader());
await addFolderToZip(folderPath + "/" + item.name, results);
}
}
resolve();
} catch (e) {
reject(e);
}
};
handleFolderToZip(folderPath, folder);
});
};

// 进行列表压缩的函数
const handleZipFileList = () => {
return new Promise(async (resolve) => {
for (let i = 0; i < fileList.value.length; i++) {
const file = fileList.value[i];
// 如果是普通文件
if (!file.fileEntry || file.isFile) {
// 如果是文件,则将文件添加到 ZIP
// 生成blob对象,避免图片压缩出问题
const blob = new Blob([file], { type: file.type });
// 需要加上binary:true,否则微软自带的office可能会有展示bug
zip.value.file(file.name, blob, { binary: true });
} else {
// 文件夹类型的
// 如果是文件夹,则递归调用该函数
zip.value.folder(file.name);
await addFolderToZip(file.name, file.fileEntry);
}
}
resolve(zip.value);
});
};

// 打包文件列表
const handlePackageFileList = () => {
// 如果没有文件就直接返回
if (fileList.value.length === 0) {
MessagePlugin.error("请先上传文件");
return;
}
zip.value = new JSZip();
handleZipFileList().then(() => {
console.log(zip.value, "zip.value");
zip.value
.generateAsync({
type: "blob",
})
.then((content) => {
const fils = new File([content], `${fileList.value[0].name}.zip`, {
type: "zip",
});
// 生成一个url
const url = URL.createObjectURL(fils);
// 创建一个a标签
const a = document.createElement("a");
// 设置a标签的href属性
a.href = url;
// 设置a标签的下载属性
a.download = `${fileList.value[0].name}.zip`;
// 触发a标签的点击事件
a.click();
// 移除url
URL.revokeObjectURL(url);
});
});
};

此时效果如下

结语

到这里,我们的功能就完成了,这里需要注意一个细节就是for循环不能用foreach替换,会产生bug。更多内容敬请期待,债见~

上一篇:
简单实现一个列配置
下一篇:
pdfjs使用bug,pop导致的bug