Files
CloudFlare-ImgBed/functions/api/huggingface/getUploadUrl.js
2026-01-08 19:51:34 +08:00

111 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* HuggingFace 大文件直传 API
*
* 流程:
* 1. 前端计算 SHA256 和文件样本
* 2. 前端调用此 API 获取 LFS 上传 URL
* 3. 前端直接上传到 HuggingFace S3
* 4. 前端调用 commitUpload API 提交文件引用
*
* 这样可以绕过 CF Workers 的 100MB 请求体限制和 CPU 时间限制
*/
import { HuggingFaceAPI } from '../../utils/huggingfaceAPI.js';
import { fetchUploadConfig } from '../../utils/sysConfig.js';
import { userAuthCheck, UnauthorizedResponse } from '../../utils/userAuth.js';
import { buildUniqueFileId } from '../../upload/uploadTools.js';
export async function onRequestPost(context) {
const { request, env } = context;
const url = new URL(request.url);
context.url = url; // 将 url 添加到 context 以便 buildUniqueFileId 使用
try {
// 鉴权
const requiredPermission = 'upload';
if (!await userAuthCheck(env, url, request, requiredPermission)) {
return UnauthorizedResponse('Unauthorized');
}
const body = await request.json();
const { fileSize, fileName, fileType, sha256, fileSample, channelName, uploadNameType, uploadFolder } = body;
if (!fileSize || !fileName || !sha256 || !fileSample) {
return new Response(JSON.stringify({
error: 'Missing required fields: fileSize, fileName, sha256, fileSample'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// 获取 HuggingFace 配置
const uploadConfig = await fetchUploadConfig(env);
const hfSettings = uploadConfig.huggingface;
if (!hfSettings || !hfSettings.channels || hfSettings.channels.length === 0) {
return new Response(JSON.stringify({ error: 'No HuggingFace channel configured' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// 选择渠道
let hfChannel;
if (channelName) {
hfChannel = hfSettings.channels.find(c => c.name === channelName);
}
if (!hfChannel) {
hfChannel = hfSettings.loadBalance?.enabled
? hfSettings.channels[Math.floor(Math.random() * hfSettings.channels.length)]
: hfSettings.channels[0];
}
if (!hfChannel || !hfChannel.token || !hfChannel.repo) {
return new Response(JSON.stringify({ error: 'HuggingFace channel not properly configured' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// 将命名参数添加到 URL 以便 buildUniqueFileId 使用
if (uploadNameType) {
url.searchParams.set('uploadNameType', uploadNameType);
}
if (uploadFolder) {
url.searchParams.set('uploadFolder', uploadFolder);
}
// 使用统一的文件命名函数生成文件ID
const fullId = await buildUniqueFileId(context, fileName, fileType || 'application/octet-stream');
// 构建 HuggingFace 文件路径:直接使用 fullId与其他渠道保持一致
const filePath = fullId;
// 获取 LFS 上传信息
const huggingfaceAPI = new HuggingFaceAPI(hfChannel.token, hfChannel.repo, hfChannel.isPrivate || false);
const uploadInfo = await huggingfaceAPI.getLfsUploadInfo(fileSize, filePath, sha256, fileSample);
// 返回上传信息
return new Response(JSON.stringify({
success: true,
fullId,
filePath,
channelName: hfChannel.name,
repo: hfChannel.repo,
isPrivate: hfChannel.isPrivate || false,
...uploadInfo
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.error('getUploadUrl error:', error.message);
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}