ci / mobile / mobile (Metro-бандл Android) (push) Successful in 3m4s
ci / ts / ts (packages + web) (push) Successful in 1m23s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 52s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m51s
- Suggest tracks: drag-and-drop of files and folders with relative paths, streaming SHA-256 in a Web Worker (hash-wasm), duplicate precheck, batch reservation and tus-js-client upload (8 MB chunks, 4 in parallel, token refresh on 401, resume of failed files). - My uploads: statuses, rejection reasons, withdraw, quota meter. - Upload queue (admin): status tabs, possible-duplicate filter, batches, metadata and storage editing, bulk approve / reject with a reason, import retry, and the queue settings form. - Sidebar section for moderators and admins; ru / en strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
/// <reference lib="webworker" />
|
|
// SHA-256 файла потоком (docs/uploads.md#4): Web Crypto не умеет считать по частям, а файлы бывают по 2 ГБ —
|
|
// hash-wasm считает чанками в воркере, не держа файл в памяти и не блокируя интерфейс.
|
|
import { createSHA256 } from 'hash-wasm';
|
|
|
|
export interface HashRequest {
|
|
id: string;
|
|
file: File;
|
|
}
|
|
|
|
export type HashResponse =
|
|
| { id: string; type: 'progress'; done: number }
|
|
| { id: string; type: 'done'; sha256: string }
|
|
| { id: string; type: 'error'; message: string };
|
|
|
|
const ctx = self as unknown as DedicatedWorkerGlobalScope;
|
|
|
|
ctx.onmessage = async (e: MessageEvent<HashRequest>) => {
|
|
const { id, file } = e.data;
|
|
try {
|
|
const h = await createSHA256();
|
|
h.init();
|
|
const reader = file.stream().getReader();
|
|
let done = 0;
|
|
let lastReport = 0;
|
|
for (;;) {
|
|
const chunk = await reader.read();
|
|
if (chunk.done) break;
|
|
h.update(chunk.value);
|
|
done += chunk.value.byteLength;
|
|
if (done - lastReport > 8 << 20) {
|
|
lastReport = done;
|
|
ctx.postMessage({ id, type: 'progress', done } satisfies HashResponse);
|
|
}
|
|
}
|
|
ctx.postMessage({ id, type: 'done', sha256: h.digest('hex') } satisfies HashResponse);
|
|
} catch (err) {
|
|
ctx.postMessage({ id, type: 'error', message: err instanceof Error ? err.message : String(err) } satisfies HashResponse);
|
|
}
|
|
};
|