mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
ui: multi-thread bulk download
This commit is contained in:
1 parent
17a2d4732d
commit
375a06a98d
3 files changed
+41
-27
No files matched your search
@@ -4,6 +4,7 @@ import { createZipStream } from 'vj/utils/zip';
|
||||
import pipeStream from 'vj/utils/pipeStream';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import request from 'vj/utils/request';
|
||||
import PQueue from 'p-queue/dist/index';
|
||||
import Notification from 'vj/components/notification';
|
||||
import api, { gql } from 'vj/utils/api';
|
||||
|
||||
@@ -28,38 +29,49 @@ const waitForWritableStream = window.WritableStream
|
||||
streamsaver.WritableStream = window.WritableStream;
|
||||
});
|
||||
|
||||
export default async function download(name, targets) {
|
||||
export default async function download(filename, targets) {
|
||||
await waitForWritableStream;
|
||||
const fileStream = streamsaver.createWriteStream(name);
|
||||
const fileStream = streamsaver.createWriteStream(filename);
|
||||
const queue = new PQueue({ concurrency: 5 });
|
||||
const abortCallbackReceiver = {};
|
||||
function stopDownload() { abortCallbackReceiver.abort(); }
|
||||
let i = 0;
|
||||
async function downloadFile(target) {
|
||||
try {
|
||||
let stream;
|
||||
if (target.url) {
|
||||
const response = await fetch(target.url);
|
||||
if (!response.ok) throw response.statusText;
|
||||
stream = response.body;
|
||||
} else {
|
||||
stream = new Blob([target.content]).stream();
|
||||
}
|
||||
return {
|
||||
name: target.filename,
|
||||
stream,
|
||||
};
|
||||
} catch (e) {
|
||||
stopDownload();
|
||||
Notification.error(i18n('Download Error', [target.filename, e.toString()]));
|
||||
}
|
||||
}
|
||||
const handles = [];
|
||||
for (const target of targets) {
|
||||
handles.push(queue.add(() => downloadFile(target)));
|
||||
}
|
||||
queue.start();
|
||||
const zipStream = createZipStream({
|
||||
// eslint-disable-next-line consistent-return
|
||||
async pull(ctrl) {
|
||||
if (i === targets.length) return ctrl.close();
|
||||
try {
|
||||
if (targets[i].url) {
|
||||
const response = await fetch(targets[i].url);
|
||||
if (!response.ok) throw response.statusText;
|
||||
ctrl.enqueue({
|
||||
name: targets[i].filename,
|
||||
stream: () => response.body,
|
||||
});
|
||||
} else {
|
||||
ctrl.enqueue({
|
||||
name: targets[i].filename,
|
||||
stream: () => new Blob([targets[i].content]).stream(),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
stopDownload();
|
||||
Notification.error(i18n('Download Error', [targets[i].filename, e.toString()]));
|
||||
}
|
||||
if (!handles[i]) return ctrl.close();
|
||||
const { name, stream } = await handles[i];
|
||||
i++;
|
||||
ctrl.enqueue({
|
||||
name,
|
||||
stream: () => stream,
|
||||
});
|
||||
},
|
||||
});
|
||||
const abortCallbackReceiver = {};
|
||||
function stopDownload() { abortCallbackReceiver.abort(); }
|
||||
window.addEventListener('unload', stopDownload);
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
await pipeStream(zipStream, fileStream, abortCallbackReceiver);
|
||||
@@ -68,7 +80,7 @@ export default async function download(name, targets) {
|
||||
}
|
||||
|
||||
export async function downloadProblemSet(pids, name = 'Export') {
|
||||
Notification.info('Downloading...');
|
||||
Notification.info(i18n('Downloading...'));
|
||||
const targets = [];
|
||||
try {
|
||||
for (const pid of pids) {
|
||||
@@ -131,7 +143,7 @@ export async function downloadProblemSet(pids, name = 'Export') {
|
||||
}
|
||||
await download(`${name}.zip`, targets);
|
||||
} catch (e) {
|
||||
Notification.warn(`${e.error?.message} ${e.error?.params?.[0]}`);
|
||||
Notification.error(`${e.message} ${e.params?.[0]}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ async function load() {
|
||||
} catch (e) {
|
||||
Notification.warn(`Failed to call '${type}Loading' of ${page.name}`);
|
||||
console.error(`Failed to call '${type}Loading' of ${page.name}\n${e.stack}`);
|
||||
console.error(e);
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.time(`${page.name}: ${type}Loading`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.34.19",
|
||||
"version": "4.34.20",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
@@ -122,6 +122,7 @@
|
||||
"markdown-it-table-of-contents": "^0.6.0",
|
||||
"mongodb": "^3.7.3",
|
||||
"nunjucks": "^3.2.3",
|
||||
"p-queue": "^7.2.0",
|
||||
"react-query": "^3.34.16",
|
||||
"streamsaver": "^2.0.6",
|
||||
"xss": "^1.0.11"
|
||||
|
||||
Reference in new issue
Block a user