/* * Copyright 2025 coze-dev Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { uploadFileV2, type EventPayloadMaps as BaseEventPayloadMap, type UploaderInstance, type UploadFileV2Param, type FileItem, } from '@coze-arch/bot-utils/upload-file-v2'; import { GetImageScene } from '@coze-arch/bot-api/playground_api'; import { PlaygroundApi } from '@coze-arch/bot-api'; export type EventPayloadMap = BaseEventPayloadMap & { ready: boolean; }; export interface UploadControllerProps { controllerId: string; fileList: File[]; userId: string; onProgress?: ( event: EventPayloadMap['progress'], controllerId: string, ) => void; onComplete?: ( event: { url: string; uri: string; }[], controllerId: string, ) => void; onUploadError?: (event: Error, controllerId: string) => void; onUploaderReady?: ( event: EventPayloadMap['ready'], controllerId: string, ) => void; onStartUpload?: ( param: Parameters['onStartUpload']>[number], controllerId: string, ) => void; onGetUploadInstanceError?: (error: Error, controllerId: string) => void; onGetTokenError?: (error: Error, controllerId: string) => void; onAuditFailed?: (controllerId: string) => void; } const isImage = (file: File) => file.type.startsWith('image/'); export class UploadController { controllerId: string; abortController: AbortController; uploader: UploaderInstance | null; fileItemList: FileItem[]; constructor({ controllerId, fileList, userId, onProgress, onComplete, onUploadError, onUploaderReady, onStartUpload, onGetTokenError, onGetUploadInstanceError, onAuditFailed, }: UploadControllerProps) { this.fileItemList = fileList.map(file => ({ file, fileType: isImage(file) ? 'image' : 'object', })); this.controllerId = controllerId; this.abortController = new AbortController(); this.uploader = null; uploadFileV2({ fileItemList: this.fileItemList, userId, signal: this.abortController.signal, timeout: undefined, onUploaderReady: uploader => { this.uploader = uploader; onUploaderReady?.(true, controllerId); }, onProgress: event => onProgress?.(event, controllerId), onUploadAllSuccess: async event => { const uris = event.map(item => { const uri = item.uploadResult.Uri; if (!uri) { throw new Error(`failed to get uri, item: ${item}`); } return uri; }); try { if (!uris || !uris.length) { throw new Error(`upload success without uri, uploadID ${event}`); } const result = await PlaygroundApi.GetImagexShortUrl({ uris, scene: GetImageScene.BackgroundImage, }); const list = uris.map(item => { if (!item) { throw new Error(`failed to get url, uri: ${item}`); } return { url: result.data?.url_info?.[item].url as string, uri: item, }; }); if (!list) { throw new Error(`failed to get urls, list: ${list}`); } if (!list.some(i => i.url)) { onAuditFailed?.(controllerId); } else { onComplete?.(list, controllerId); } } catch (e) { onUploadError?.( e instanceof Error ? e : new Error(String(e)), controllerId, ); } }, onUploadError: event => onUploadError?.(event.extra.error, controllerId), onStartUpload: event => onStartUpload?.(event, controllerId), onGetUploadInstanceError: error => onGetUploadInstanceError?.(error, controllerId), onGetTokenError: error => onGetTokenError?.(error, controllerId), }); } cancel = () => { this.abortController.abort(); }; pause = () => { this.uploader?.pause(); }; }