Files
digital-pilates/hooks/useCosUpload.ts
richarjiang 532cf251e2 feat: 添加会话历史管理功能
- 在 AI 教练聊天界面中新增会话历史查看和选择功能,用户可以查看和选择之前的会话记录
- 实现会话删除功能,用户可以删除不需要的会话记录
- 优化历史会话的加载和展示,提升用户体验
- 更新相关样式以适应新功能的展示和交互
2025-08-14 10:15:02 +08:00

78 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { buildCosKey, buildPublicUrl } from '@/constants/Cos';
import { uploadWithRetry } from '@/services/cos';
import { useCallback, useMemo, useRef, useState } from 'react';
export type UseCosUploadOptions = {
prefix?: string;
userId?: string;
contentType?: string;
};
export function useCosUpload(defaultOptions?: UseCosUploadOptions) {
const abortRef = useRef<AbortController | null>(null);
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const cancel = useCallback(() => {
abortRef.current?.abort();
}, []);
const upload = useCallback(
async (file: { uri?: string; name?: string; type?: string; buffer?: any; blob?: Blob } | Blob | string | any, options?: UseCosUploadOptions) => {
const finalOptions = { ...(defaultOptions || {}), ...(options || {}) };
const extGuess = (() => {
const name = (file && (file.name || (file as any).filename)) || '';
const match = name.match(/\.([a-zA-Z0-9]+)$/);
return match ? match[1] : undefined;
})();
const key = buildCosKey({ prefix: finalOptions.prefix, userId: finalOptions.userId, ext: extGuess });
const controller = new AbortController();
abortRef.current = controller;
setProgress(0);
setUploading(true);
try {
let body: any = null;
// 1) 直接可用类型Blob 或 string
if (typeof file === 'string') {
body = file;
} else if (typeof Blob !== 'undefined' && file instanceof Blob) {
body = file;
} else if ((file as any)?.blob && (typeof Blob === 'undefined' || (file as any).blob instanceof Blob || (file as any).blob?._data)) {
// 2) 已提供 blob 字段
body = (file as any).blob;
} else if ((file as any)?.buffer) {
// 3) ArrayBuffer/TypedArray -> Blob
const buffer = (file as any).buffer;
body = new Blob([buffer], { type: (file as any)?.type || finalOptions.contentType || 'application/octet-stream' });
} else if ((file as any)?.uri) {
// 4) Expo ImagePicker/文件:必须先转 Blob
const resp = await fetch((file as any).uri);
body = await resp.blob();
} else {
// 兜底:尝试直接作为字符串,否则抛错
if (file && (typeof file === 'object')) {
throw new Error('无效的上传体:请提供 Blob/String或包含 uri 的对象');
}
body = file;
}
const res = await uploadWithRetry({
key,
body,
contentType: finalOptions.contentType || (file as any)?.type,
signal: controller.signal,
onProgress: ({ percent }) => setProgress(percent),
});
const url = (res as any).publicUrl || buildPublicUrl(res.key);
return { key: res.key, url };
} finally {
setUploading(false);
}
},
[defaultOptions]
);
return useMemo(() => ({ upload, cancel, progress, uploading }), [upload, cancel, progress, uploading]);
}