fix(avatar): upload knowledge files in chunks
This commit is contained in:
@@ -333,12 +333,76 @@ export interface SearchResult {
|
||||
export const getKnowledgeDocs = (avatarId: string) =>
|
||||
request.get<KnowledgeDoc[]>(`/avatar/${avatarId}/knowledge/docs`)
|
||||
|
||||
// 上传文档(支持 md/txt/pdf/doc/docx/xlsx)
|
||||
export const uploadKnowledgeDoc = (
|
||||
const KNOWLEDGE_UPLOAD_CHUNK_SIZE = 5 * 1024 * 1024
|
||||
|
||||
const uploadKnowledgeChunk = async (
|
||||
avatarId: string,
|
||||
uploadId: string,
|
||||
chunkIndex: number,
|
||||
chunk: Blob,
|
||||
onProgress?: (loaded: number) => void
|
||||
) => {
|
||||
const form = new FormData()
|
||||
form.append('file', chunk, `chunk-${chunkIndex}`)
|
||||
let reportedLoaded = 0
|
||||
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
||||
try {
|
||||
await request.post(
|
||||
`/avatar/${avatarId}/knowledge/uploads/${uploadId}/chunks/${chunkIndex}`,
|
||||
form,
|
||||
{
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
timeout: 2 * 60 * 1000,
|
||||
onUploadProgress: (event) => {
|
||||
reportedLoaded = Math.max(reportedLoaded, Math.min(event.loaded, chunk.size))
|
||||
onProgress?.(reportedLoaded)
|
||||
}
|
||||
}
|
||||
)
|
||||
return
|
||||
} catch (error: any) {
|
||||
const status = Number(error?.response?.status || 0)
|
||||
const retryable = !status || status === 408 || status === 429 || status >= 500
|
||||
if (!retryable || attempt === 3) throw error
|
||||
await new Promise((resolve) => window.setTimeout(resolve, attempt * 800))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 大文件拆成 5MB 分片,避免生产代理的请求体限制拦截整个文件。
|
||||
export const uploadKnowledgeDoc = async (
|
||||
avatarId: string,
|
||||
file: File,
|
||||
onUploadProgress?: (loaded: number, total: number) => void
|
||||
) => {
|
||||
if (file.size > KNOWLEDGE_UPLOAD_CHUNK_SIZE) {
|
||||
const totalChunks = Math.ceil(file.size / KNOWLEDGE_UPLOAD_CHUNK_SIZE)
|
||||
const upload: any = await request.post(`/avatar/${avatarId}/knowledge/uploads`, {
|
||||
filename: file.name,
|
||||
fileSize: file.size,
|
||||
totalChunks
|
||||
})
|
||||
let uploadedBytes = 0
|
||||
for (let index = 0; index < totalChunks; index += 1) {
|
||||
const start = index * KNOWLEDGE_UPLOAD_CHUNK_SIZE
|
||||
const chunk = file.slice(start, Math.min(start + KNOWLEDGE_UPLOAD_CHUNK_SIZE, file.size))
|
||||
await uploadKnowledgeChunk(
|
||||
avatarId,
|
||||
upload.uploadId,
|
||||
index,
|
||||
chunk,
|
||||
(chunkLoaded) => onUploadProgress?.(uploadedBytes + chunkLoaded, file.size)
|
||||
)
|
||||
uploadedBytes += chunk.size
|
||||
onUploadProgress?.(uploadedBytes, file.size)
|
||||
}
|
||||
return request.post<KnowledgeDoc>(
|
||||
`/avatar/${avatarId}/knowledge/uploads/${upload.uploadId}/complete`,
|
||||
undefined,
|
||||
{ timeout: 2 * 60 * 1000 }
|
||||
)
|
||||
}
|
||||
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return request.post<KnowledgeDoc>(`/avatar/${avatarId}/knowledge/docs`, form, {
|
||||
|
||||
Reference in New Issue
Block a user