Add export zip by web server

This commit is contained in:
2023-05-29 13:50:06 +08:00
parent b8d7c8313f
commit 6c4c0836ed
3 changed files with 149 additions and 4 deletions

View File

@@ -4,16 +4,18 @@
import config from "./deno.json" assert { type: "json" };
import * as $0 from "./routes/api/config.ts";
import * as $1 from "./routes/api/task/list.ts";
import * as $2 from "./routes/index.tsx";
import * as $1 from "./routes/api/export/gallery/zip/[gid].ts";
import * as $2 from "./routes/api/task/list.ts";
import * as $3 from "./routes/index.tsx";
import * as $$0 from "./islands/Container.tsx";
import * as $$1 from "./islands/Settings.tsx";
const manifest = {
routes: {
"./routes/api/config.ts": $0,
"./routes/api/task/list.ts": $1,
"./routes/index.tsx": $2,
"./routes/api/export/gallery/zip/[gid].ts": $1,
"./routes/api/task/list.ts": $2,
"./routes/index.tsx": $3,
},
islands: {
"./islands/Container.tsx": $$0,

View File

@@ -0,0 +1,19 @@
import { Handlers } from "$fresh/server.ts";
import { get_task_manager } from "../../../../../server.ts";
import { get_export_zip_response } from "../../../../../server/export_zip.ts";
export const handler: Handlers = {
GET(_req, ctx) {
const gid = parseInt(ctx.params.gid);
if (isNaN(gid)) {
return new Response("Bad Request", { status: 400 });
}
const m = get_task_manager();
try {
return get_export_zip_response(gid, m.db);
} catch (e) {
console.error(e);
return new Response("Gallery not found.", { status: 404 });
}
},
};

124
server/export_zip.ts Normal file
View File

@@ -0,0 +1,124 @@
import { Uint8ArrayReader, ZipWriter } from "zipjs/index.js";
import { EhDb, PMeta } from "../db.ts";
import { addZero } from "../utils.ts";
export function get_export_zip_response(gid: number, db: EhDb) {
const gmeta = db.get_gmeta_by_gid(gid);
if (!gmeta) throw Error("Gallery not found.");
const pmetas = db.get_pmeta(gid).sort((a, b) => a.index - b.index);
const p = pmetas.length;
const l = gmeta.filecount.toString().length;
let c = 0;
let b = new Uint8Array(0);
/// Current buffer count
let bn = 0;
/// Buffer length
let bl = 0;
const readInto = (s: Uint8Array) => {
const r = Math.min(s.length, bn);
s.set(b.slice(0, r));
b.set(b.slice(r));
bn -= r;
return r;
};
const s = new WritableStream<Uint8Array>({
write: (d, c) => {
if (c.signal.aborted) return;
const s = d.length;
if (!s) return;
if (bn + s > bl) {
bl = bn + s;
const nb = new Uint8Array(bl);
nb.set(b);
b = nb;
}
b.set(d, bn);
bn += s;
},
});
const zip_writer = new ZipWriter(s);
let zip_closed = false;
let closed = false;
const signalc = new AbortController();
const signal = signalc.signal;
const download_task = async (p: PMeta) => {
const f = db.get_files(gid, p.token);
if (f.length) {
const r = await Deno.readFile(f[0].path, { signal });
await zip_writer.add(
`${addZero(p.index, l)}_${p.name}`,
new Uint8ArrayReader(r),
{ signal },
);
}
};
const add_task = async () => {
if (closed) return true;
if (c === p) {
if (!zip_closed) {
await zip_writer.close();
zip_closed = true;
return false;
}
return true;
}
await download_task(pmetas[c]);
c += 1;
return false;
};
const readable = new ReadableStream({
pull: async (c) => {
while (1) {
if (c.byobRequest) {
const r = c.byobRequest;
if (r.view) {
const v = new Uint8Array(
r.view.buffer,
r.view.byteOffset,
);
const f = readInto(v);
if (f !== 0) {
r.respond(f);
return;
}
} else {
const v = new Uint8Array(bn);
const f = readInto(v);
if (f !== 0) {
r.respondWithNewView(v.slice(0, f));
return;
}
}
} else {
const v = new Uint8Array(bn);
const f = readInto(v);
if (f !== 0) {
c.enqueue(v.slice(0, f));
return;
}
}
if (await add_task()) {
c.close();
return;
}
}
},
cancel: async () => {
signalc.abort();
if (!zip_closed) {
await zip_writer.close();
zip_closed = true;
}
closed = true;
},
type: "bytes",
});
return new Response(readable, {
headers: {
"content-type": "application/zip",
"Content-Disposition": `attachment; filename="${
gmeta.title.replace(/"/g, '\\"')
}.zip"`,
},
});
}