Replace Blob with File for file uploads

This commit is contained in:
CPTProgrammer
2026-07-11 06:06:34 +08:00
parent 24637d3db3
commit cbce560805
5 changed files with 21 additions and 25 deletions
+3 -5
View File
@@ -39,20 +39,18 @@ export async function getFiles(
* @param client CurseForge 客户端实例 * @param client CurseForge 客户端实例
* @param projectId 项目 ID(数字) * @param projectId 项目 ID(数字)
* @param metadata 已通过 Zod 校验的文件元数据 * @param metadata 已通过 Zod 校验的文件元数据
* @param file 要上传的文件 Blob * @param file 要上传的文件
*/ */
export async function uploadFile( export async function uploadFile(
client: CurseForgeClient, client: CurseForgeClient,
projectId: number, projectId: number,
metadata: UploadMetadata, metadata: UploadMetadata,
file: Blob, file: File,
): Promise<UploadResponse> { ): Promise<UploadResponse> {
const form = new FormData(); const form = new FormData();
form.append("metadata", JSON.stringify(metadata)); form.append("metadata", JSON.stringify(metadata));
const filename = form.append("file", file, file.name);
file instanceof File ? file.name : "upload.jar";
form.append("file", file, filename);
const result = await client.uploadPost<unknown>( const result = await client.uploadPost<unknown>(
`/api/projects/${encodeURIComponent(projectId)}/upload-file`, `/api/projects/${encodeURIComponent(projectId)}/upload-file`,
+5 -8
View File
@@ -90,12 +90,12 @@ export async function getVersions(
* *
* @param client Modrinth 客户端实例 * @param client Modrinth 客户端实例
* @param data 已通过 Zod 校验的版本元数据(CreatableVersion * @param data 已通过 Zod 校验的版本元数据(CreatableVersion
* @param files 文件映射:key 为 multipart 字段名(需与 data.file_parts 一致),value 为文件 Blob * @param files 文件映射:key 为 multipart 字段名(需与 data.file_parts 一致),value 为文件
*/ */
export async function createVersion( export async function createVersion(
client: ModrinthClient, client: ModrinthClient,
data: CreatableVersion, data: CreatableVersion,
files: Record<string, Blob>, files: Record<string, File>,
): Promise<Version> { ): Promise<Version> {
const form = new FormData(); const form = new FormData();
@@ -104,16 +104,13 @@ export async function createVersion(
// 逐一添加文件 // 逐一添加文件
for (const fieldName of data.file_parts) { for (const fieldName of data.file_parts) {
const blob = files[fieldName]; const file = files[fieldName];
if (!blob) { if (!file) {
throw new Error( throw new Error(
`createVersion: file field "${fieldName}" declared in file_parts but not provided in files`, `createVersion: file field "${fieldName}" declared in file_parts but not provided in files`,
); );
} }
// 从 Blob 推断文件名;若无则使用字段名作为回退 form.append(fieldName, file, file.name);
const filename =
blob instanceof File ? blob.name : `${fieldName}.jar`;
form.append(fieldName, blob, filename);
} }
const result = await client.post<unknown>("/version", form); const result = await client.post<unknown>("/version", form);
+5 -4
View File
@@ -1,9 +1,10 @@
import { readFileSync } from "node:fs"; import { readFile } from "node:fs/promises";
import { basename } from "node:path";
export function readJarBlob(filePath: string): Blob { export async function readAsFile(filePath: string): Promise<File> {
try { try {
const buffer = readFileSync(filePath); const buffer = await readFile(filePath);
return new Blob([buffer]); return new File([buffer], basename(filePath));
} catch (err) { } catch (err) {
const msg = err instanceof Error ? err.message : String(err); const msg = err instanceof Error ? err.message : String(err);
throw new Error(`Failed to read file "${filePath}": ${msg}`); throw new Error(`Failed to read file "${filePath}": ${msg}`);
+3 -3
View File
@@ -1,7 +1,7 @@
import { curseforgeClient } from "@/services/clients"; import { curseforgeClient } from "@/services/clients";
import { uploadFile, getGameVersions } from "@/lib/curseforge"; import { uploadFile, getGameVersions } from "@/lib/curseforge";
import { resolveVersionName, buildJarPath } from "@/lib/utils/format"; import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
import { readJarBlob } from "@/lib/utils/file"; import { readAsFile } from "@/lib/utils/file";
import type { UploadMetadata } from "@/types"; import type { UploadMetadata } from "@/types";
import type { PlatformContext } from "./types"; import type { PlatformContext } from "./types";
@@ -24,7 +24,7 @@ export async function publishCurseForgeVersion(
mcVersion, mcVersion,
); );
const primaryBlob = readJarBlob(primaryPath); const primaryFile = await readAsFile(primaryPath);
const gameVersions = await getGameVersions(curseforgeClient); const gameVersions = await getGameVersions(curseforgeClient);
@@ -51,6 +51,6 @@ export async function publishCurseForgeVersion(
curseforgeClient, curseforgeClient,
config.curseforge.project_id, config.curseforge.project_id,
metadata, metadata,
primaryBlob, primaryFile,
); );
} }
+5 -5
View File
@@ -1,7 +1,7 @@
import { modrinthClient } from "@/services/clients"; import { modrinthClient } from "@/services/clients";
import { createVersion } from "@/lib/modrinth"; import { createVersion } from "@/lib/modrinth";
import { resolveVersionName, buildJarPath } from "@/lib/utils/format"; import { resolveVersionName, buildJarPath } from "@/lib/utils/format";
import { readJarBlob } from "@/lib/utils/file"; import { readAsFile } from "@/lib/utils/file";
import type { CreatableVersion } from "@/types"; import type { CreatableVersion } from "@/types";
import type { PlatformContext } from "./types"; import type { PlatformContext } from "./types";
@@ -40,8 +40,8 @@ export async function publishModrinthVersion(
mcVersion, mcVersion,
); );
const primaryBlob = readJarBlob(primaryPath); const primaryFile = await readAsFile(primaryPath);
const sourceBlob = readJarBlob(sourcePath); const sourceFile = await readAsFile(sourcePath);
const data: CreatableVersion = { const data: CreatableVersion = {
project_id: config.modrinth.project_id, project_id: config.modrinth.project_id,
@@ -65,7 +65,7 @@ export async function publishModrinthVersion(
}; };
await createVersion(modrinthClient, data, { await createVersion(modrinthClient, data, {
primary: primaryBlob, primary: primaryFile,
sources: sourceBlob, sources: sourceFile,
}); });
} }