Files
digital-pilates/hooks/useCosUpload.ts
richarjiang 5d09cc05dc feat: 更新文章功能和相关依赖
- 新增文章详情页面,支持根据文章 ID 加载和展示文章内容
- 添加文章卡片组件,展示推荐文章的标题、封面和阅读量
- 更新文章服务,支持获取文章列表和根据 ID 获取文章详情
- 集成腾讯云 COS SDK,支持文件上传功能
- 优化打卡功能,支持按日期加载和展示打卡记录
- 更新相关依赖,确保项目兼容性和功能完整性
- 调整样式以适应新功能的展示和交互
2025-08-14 16:03:19 +08:00

86 lines
3.2 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';
import { Platform } from 'react-native';
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 res: any;
if (Platform.OS === 'web') {
// Web使用 Blob 走 cos-js-sdk-v5 分支
let body: any = null;
if (typeof file === 'string') {
// 允许直接传 Base64/DataURL 字符串
body = file;
} else if ((file as any)?.blob) {
body = (file as any).blob;
} else if ((file as any)?.uri) {
const resp = await fetch((file as any).uri);
body = await resp.blob();
} else if (typeof Blob !== 'undefined' && file instanceof Blob) {
body = file;
} else {
throw new Error('无效的文件:请提供 uri 或 Blob');
}
res = await uploadWithRetry({
key,
body,
contentType: finalOptions.contentType || (file as any)?.type,
signal: controller.signal,
onProgress: ({ percent }: { percent: number }) => setProgress(percent),
} as any);
} else {
// 原生:直接传本地路径
const srcUri = (file as any)?.uri || (typeof file === 'string' ? file : undefined);
if (!srcUri || typeof srcUri !== 'string') {
throw new Error('请提供包含 uri 的对象,或传入本地文件路径字符串');
}
res = await uploadWithRetry({
key,
srcUri,
contentType: finalOptions.contentType || (file as any)?.type,
signal: controller.signal,
onProgress: ({ percent }: { percent: number }) => setProgress(percent),
} as any);
}
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]);
}