73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
import type { PlatformApiClient } from "../api/client";
|
|
import type { ServerFileDownloadResponse } from "../api/types";
|
|
|
|
export async function downloadServerFileResult(client: PlatformApiClient, result: ServerFileDownloadResponse): Promise<string> {
|
|
if (result.status !== "ready") {
|
|
return result.reason ?? "文件读取任务已派发,完成后可再次下载。";
|
|
}
|
|
if (result.content !== undefined) {
|
|
saveBlob(new Blob([result.content], { type: result.contentType ?? "text/plain;charset=utf-8" }), result.filename || "server-file.txt");
|
|
return `下载已开始:${result.filename || result.key}`;
|
|
}
|
|
if (!result.artifact) {
|
|
return "文件内容尚未可用。";
|
|
}
|
|
const writer = await createFileWriter(result.filename || result.artifact.filename);
|
|
let offset = 0;
|
|
const chunkSize = Math.max(1, result.artifact.chunkSizeBytes || 1024 * 1024);
|
|
try {
|
|
while (offset < result.artifact.sizeBytes) {
|
|
const chunk = await client.readArtifactContent(result.artifact.artifactId, offset, Math.min(chunkSize, result.artifact.sizeBytes - offset));
|
|
if (chunk.contentLength <= 0) throw new Error("文件下载返回空数据");
|
|
await writer.write(chunk.payload);
|
|
offset += chunk.contentLength;
|
|
}
|
|
await writer.close();
|
|
} catch (error) {
|
|
await writer.abort();
|
|
throw error;
|
|
}
|
|
return `下载已开始:${result.filename || result.artifact.filename}`;
|
|
}
|
|
|
|
interface FileWriter {
|
|
write(data: ArrayBuffer): Promise<void>;
|
|
close(): Promise<void>;
|
|
abort(): Promise<void>;
|
|
}
|
|
|
|
async function createFileWriter(filename: string): Promise<FileWriter> {
|
|
const picker = (window as Window & { showSaveFilePicker?: (options?: { suggestedName?: string }) => Promise<{ createWritable(): Promise<FileSystemWritableFileStream> }> }).showSaveFilePicker;
|
|
if (picker) {
|
|
const handle = await picker({ suggestedName: safeFilename(filename) });
|
|
const writable = await handle.createWritable();
|
|
return {
|
|
write: async (data) => { await writable.write(data); },
|
|
close: async () => { await writable.close(); },
|
|
abort: async () => { await writable.abort(); }
|
|
};
|
|
}
|
|
const chunks: ArrayBuffer[] = [];
|
|
return {
|
|
write: async (data) => { chunks.push(data); },
|
|
close: async () => { saveBlob(new Blob(chunks, { type: "application/octet-stream" }), safeFilename(filename)); },
|
|
abort: async () => { chunks.length = 0; }
|
|
};
|
|
}
|
|
|
|
function saveBlob(blob: Blob, filename: string) {
|
|
const url = URL.createObjectURL(blob);
|
|
const anchor = document.createElement("a");
|
|
anchor.href = url;
|
|
anchor.download = safeFilename(filename);
|
|
document.body.appendChild(anchor);
|
|
anchor.click();
|
|
anchor.remove();
|
|
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
}
|
|
|
|
function safeFilename(filename: string): string {
|
|
const cleaned = filename.replace(/[\\/:*?"<>|]+/g, "-").trim();
|
|
return cleaned || "server-file.txt";
|
|
}
|