Skip to content

Instantly share code, notes, and snippets.

@semlinker
Last active January 29, 2023 18:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save semlinker/3bb9634f4e4ec7b6ab4008a688583115 to your computer and use it in GitHub Desktop.
Save semlinker/3bb9634f4e4ec7b6ab4008a688583115 to your computer and use it in GitHub Desktop.
在线解压ZIP文件
const path = require("path");
const Koa = require("koa");
const cors = require("@koa/cors");
const Router = require("@koa/router");
const StreamZip = require("node-stream-zip");
const app = new Koa();
const router = new Router();
const ZIP_HOME = path.join(__dirname, "zip");
const UnzipCaches = new Map();
router.get("/", async (ctx) => {
ctx.body = "服务端在线解压ZIP文件示例(阿宝哥)";
});
router.get("/unzip/:name", async (ctx) => {
const fileName = ctx.params.name;
let filteredEntries;
try {
if (UnzipCaches.has(fileName)) {
// 优先从缓存中获取
filteredEntries = UnzipCaches.get(fileName);
} else {
const zip = new StreamZip.async({ file: path.join(ZIP_HOME, fileName) });
const entries = await zip.entries();
filteredEntries = Object.values(entries).map((entry) => {
return {
name: entry.name,
size: entry.size,
dir: entry.isDirectory,
};
});
await zip.close();
UnzipCaches.set(fileName, filteredEntries);
}
ctx.body = {
status: "success",
entries: filteredEntries,
};
} catch (error) {
ctx.body = {
status: "error",
msg: `在线解压${fileName}文件失败`,
};
}
});
router.get("/unzip/:name/entry", async (ctx) => {
const fileName = ctx.params.name;
const entryPath = ctx.query.path;
try {
const zip = new StreamZip.async({ file: path.join(ZIP_HOME, fileName) });
const entryData = await zip.entryData(entryPath);
await zip.close();
ctx.body = {
status: "success",
entryData: entryData,
};
} catch (error) {
console.dir(error);
ctx.body = {
status: "error",
msg: `读取${fileName}中${entryPath}文件失败`,
};
}
});
// 注册中间件
app.use(cors());
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
console.log("app starting at port 3000");
});
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>浏览器在线解压ZIP</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jszip/3.5.0/jszip.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js"></script>
<style>
.caret {
cursor: pointer;
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.caret-down::before {
-ms-transform: rotate(90deg); /* IE 9 */
-webkit-transform: rotate(90deg); /* Safari */'
transform: rotate(90deg);
}
.indent {
text-indent: 1em;
}
#fileList > li {
list-style: none;
}
</style>
</head>
<body>
<p>
<label>请输入ZIP文件的线上地址:</label>
<input type="text" id="zipUrl" />
</p>
<button id="unzipBtn" onclick="unzipOnline()">在线解压</button>
<p id="status"></p>
<ul id="fileList"></ul>
<script>
class ExeJSZip {
// 用于获取url地址对应的文件内容
getBinaryContent(url, progressFn = () => {}) {
return new Promise((resolve, reject) => {
if (typeof url !== "string" || !/https?:/.test(url))
reject(new Error("url 参数不合法"));
JSZipUtils.getBinaryContent(url, {
// JSZipUtils来自于jszip-utils这个库
progress: progressFn,
callback: (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
},
});
});
}
// 遍历Zip文件
async iterateZipFile(data, iterationFn) {
if (typeof iterationFn !== "function") {
throw new Error("iterationFn 不是函数类型");
}
let zip;
try {
zip = await JSZip.loadAsync(data); // JSZip来自于jszip这个库
zip.forEach(iterationFn);
return zip;
} catch (error) {
throw new error();
}
}
}
</script>
<script>
const zipUrlEle = document.querySelector("#zipUrl");
const statusEle = document.querySelector("#status");
const fileList = document.querySelector("#fileList");
const exeJSZip = new ExeJSZip();
async function unzipOnline() {
fileList.innerHTML = "";
statusEle.innerText = "开始下载文件...";
const data = await exeJSZip.getBinaryContent(
zipUrlEle.value,
handleProgress
);
let items = "";
await exeJSZip.iterateZipFile(data, (relativePath, zipEntry) => {
items += `<li class=${zipEntry.dir ? "caret" : "indent"}>${
zipEntry.name
}</li>`;
});
statusEle.innerText = "ZIP文件解压成功";
fileList.innerHTML = items;
}
function handleProgress(progressData) {
const { percent, loaded, total } = progressData;
if (loaded === total) {
statusEle.innerText = "文件已下载,努力解压中";
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>服务器在线解压Zip</title>
<script src="https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script>
<style>
.caret {
cursor: pointer;
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.caret-down::before {
-ms-transform: rotate(90deg); /* IE 9 */
-webkit-transform: rotate(90deg); /* Safari */'
transform: rotate(90deg);
}
.indent {
text-indent: 1em;
}
#fileList > li {
list-style: none;
}
</style>
</head>
<body>
<p>
<label>请输入ZIP文件名:</label>
<input type="text" id="fileName" value="kl_161828427993677" />
</p>
<button id="unzipBtn" onclick="unzipOnline()">在线解压</button>
<p id="status"></p>
<ul id="fileList"></ul>
<script>
const fileList = document.querySelector("#fileList");
const fileNameEle = document.querySelector("#fileName");
const request = axios.create({
baseURL: "http://localhost:3000/",
timeout: 5000,
});
async function unzipOnline() {
const fileName = fileNameEle.value;
if(!fileName) return;
const response = await request.get(`unzip/${fileName}`);
if (response.data && response.data.status === "success") {
const entries = response.data.entries;
let items = "";
entries.forEach((zipEntry) => {
items += `<li class=${zipEntry.dir ? "caret" : "indent"}>${
zipEntry.name
}</li>`;
});
fileList.innerHTML = items;
}
}
async function previewZipFile(path = "minivue-master/img/structure.png") {
const fileName = fileNameEle.value;
const response = await request.get(
`unzip/${fileName}/entry?path=${path}`
);
if (response.data && response.data.status === "success") {
const { entryData } = response.data;
const entryBuffer = toArrayBuffer(entryData.data);
const blob = new Blob([entryBuffer]);
// 使用URL.createObjectURL或blob.text()读取文件信息
}
}
function toArrayBuffer(buf) {
let ab = new ArrayBuffer(buf.length);
let view = new Uint8Array(ab);
for (let i = 0; i < buf.length; ++i) {
view[i] = buf[i];
}
return ab;
}
</script>
</body>
</html>
@Iconkop
Copy link

Iconkop commented Jan 29, 2023

,,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment