mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-05-03 10:26:58 +00:00
feat(huggingface): implement direct upload for large files (>20MB)
- Add frontend direct upload to HuggingFace S3, bypassing CF Workers limits - Add /api/huggingface/getUploadUrl endpoint to get LFS upload URLs - Add /api/huggingface/commitUpload endpoint to commit file references - Support multipart upload for very large files - SHA256 computed in frontend to avoid CF Workers CPU timeout - Small files (<20MB) still use proxy upload through CF Workers
This commit is contained in:
105
functions/api/huggingface/getUploadUrl.js
Normal file
105
functions/api/huggingface/getUploadUrl.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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 { getUploadConfig } from '../../utils/sysConfig.js';
|
||||
|
||||
export async function onRequestPost(context) {
|
||||
const { request, env } = context;
|
||||
|
||||
try {
|
||||
// 验证认证码
|
||||
const authCode = request.headers.get('authcode');
|
||||
if (env.AUTH_CODE && authCode !== env.AUTH_CODE) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { fileSize, fileName, sha256, fileSample, channelName } = 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 getUploadConfig(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' }
|
||||
});
|
||||
}
|
||||
|
||||
// 构建文件路径
|
||||
const now = new Date();
|
||||
const yearMonth = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}`;
|
||||
|
||||
// 生成唯一文件名
|
||||
const ext = fileName.includes('.') ? fileName.substring(fileName.lastIndexOf('.')) : '';
|
||||
const uniqueId = crypto.randomUUID().replace(/-/g, '');
|
||||
const fullId = uniqueId + ext;
|
||||
const filePath = `images/${yearMonth}/${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' }
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user