perf: async OSS upload in resumable complete endpoint

For OSS storage, the /api/upload/resumable/complete endpoint now returns
immediately after all chunks are assembled, then uploads to OSS in the
background. This eliminates the blocking re-upload delay for large files.

Local storage remains synchronous (already optimized with rename-first).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
237899745
2026-04-04 00:26:02 +08:00
parent f40fd6003c
commit 78821e0c44

View File

@@ -6460,10 +6460,57 @@ app.post('/api/upload/resumable/complete', authMiddleware, async (req, res) => {
}); });
const storageInterface = new StorageInterface(storageUserContext); const storageInterface = new StorageInterface(storageUserContext);
storage = await storageInterface.connect(); storage = await storageInterface.connect();
await storage.put(session.temp_file_path, session.target_path);
if (sessionStorageType === 'oss') { if (sessionStorageType === 'oss') {
// OSS 模式:先标记会话为 finalizing 并立即返回响应,后台异步上传到 OSS
UploadSessionDB.setStatus(session.session_id, 'finalizing', {
completed: false,
expiresAt: buildResumableUploadExpiresAt(30 * 60 * 1000)
});
res.json({
success: true,
message: '分片上传完成,文件正在转存到云端',
path: session.target_path,
file_name: session.file_name,
file_size: fileSize
});
// 后台异步执行 OSS 上传,不阻塞客户端
const bgStorage = storage;
storage = null; // 防止 finally 提前关闭
(async () => {
try {
await bgStorage.put(session.temp_file_path, session.target_path);
clearOssUsageCache(req.user.id); clearOssUsageCache(req.user.id);
UploadSessionDB.setStatus(session.session_id, 'completed', {
completed: true,
expiresAt: buildResumableUploadExpiresAt(60 * 1000)
});
safeUnlink(session.temp_file_path);
await trackFileHashIndexForUpload({
userId: req.user.id,
storageType: sessionStorageType,
fileHash: session.file_hash,
fileSize,
filePath: session.target_path
});
console.log(`[分片上传] OSS 后台转存完成: ${session.target_path}`);
} catch (bgError) {
console.error(`[分片上传] OSS 后台转存失败: ${session.target_path}`, bgError);
UploadSessionDB.setStatus(session.session_id, 'failed', {
completed: false,
expiresAt: buildResumableUploadExpiresAt(60 * 60 * 1000)
});
} finally {
await bgStorage.end().catch(() => {});
} }
})();
return;
}
// 本地存储直接同步完成rename-first 策略已优化,速度很快)
await storage.put(session.temp_file_path, session.target_path);
UploadSessionDB.setStatus(session.session_id, 'completed', { UploadSessionDB.setStatus(session.session_id, 'completed', {
completed: true, completed: true,