import { hasRemoteConfig } from '@/utils/runtime-config.js' import { normalizeResumableCompletePayload, normalizeResumableCompleteResult, normalizeResumableInitPayload, normalizeResumableInitResult, normalizeUploadId, normalizeUploadMd5 } from './file-upload-contract.js' import { assertPlainPayload } from './request-normalizers.js' import { createRequestError, requestBrowserFileChunk, requestNativeFileChunk, requestStrict } from './request-client.js' const requireRemoteUpload = () => { if (hasRemoteConfig()) return throw createRequestError('文件上传需要真实服务,当前本地预览不伪造回执', 'REMOTE_WRITE_REQUIRED') } const normalizeChunkIndex = (value) => { if (!Number.isInteger(value) || value < 0 || value > 2147483647) { throw new TypeError('chunkIndex必须是非负 int32') } return value } export const fileUploadApi = { async initializeResumableUpload(payload, requestOptions = {}) { const uploadRequest = normalizeResumableInitPayload(payload) requireRemoteUpload() const uploadSession = await requestStrict({ url: '/genealogy/app/files/resumable/init', method: 'POST', data: uploadRequest }, { requestController: requestOptions.requestController ?? null }) return normalizeResumableInitResult(uploadSession) }, async uploadResumableChunk(payload, requestOptions = {}) { assertPlainPayload( payload, new Set(['uploadId', 'chunkIndex', 'chunkMd5', 'filePath']), '文件分片请求' ) if (typeof payload.filePath !== 'string' || !payload.filePath.trim()) { throw new TypeError('filePath必须是非空字符串') } requireRemoteUpload() return requestNativeFileChunk({ uploadId: normalizeUploadId(payload.uploadId, 'uploadId'), chunkIndex: normalizeChunkIndex(payload.chunkIndex), chunkMd5: normalizeUploadMd5(payload.chunkMd5, 'chunkMd5'), filePath: payload.filePath.trim() }, requestOptions) }, async uploadBrowserResumableChunk(payload, file, requestOptions = {}) { assertPlainPayload( payload, new Set(['uploadId', 'chunkIndex', 'chunkMd5']), '浏览器文件分片请求' ) if (!file || typeof file !== 'object') { throw new TypeError('浏览器文件分片必须提供文件对象') } requireRemoteUpload() return requestBrowserFileChunk({ uploadId: normalizeUploadId(payload.uploadId, 'uploadId'), chunkIndex: normalizeChunkIndex(payload.chunkIndex), chunkMd5: normalizeUploadMd5(payload.chunkMd5, 'chunkMd5'), file }, requestOptions) }, async completeResumableUpload(payload, requestOptions = {}) { const completionRequest = normalizeResumableCompletePayload(payload) requireRemoteUpload() const uploadedFile = await requestStrict({ url: '/genealogy/app/files/resumable/complete', method: 'POST', data: completionRequest }, { requestController: requestOptions.requestController ?? null }) return normalizeResumableCompleteResult(uploadedFile) } }