# 腾讯云 COS 上传集成说明 本文档记录本项目在前端(Expo/React Native)侧对腾讯云 COS 的接入方式与使用规范,包含配置、接口约定、上传用法、进度/取消/重试、URL 构建、权限与安全、常见问题等。 ## 概览 - 依赖:`cos-js-sdk-v5` - 临时密钥接口:`GET /api/users/cos-token` - 统一封装: - 配置:`constants/Cos.ts` - 上传服务:`services/cos.ts` - Hook:`hooks/useCosUpload.ts` ## 安装与依赖 已在 `package.json` 中添加: ```json { "dependencies": { "cos-js-sdk-v5": "^1.6.0" } } ``` ## 配置 编辑 `constants/Cos.ts`,填入实际 COS 参数: ```ts export const COS_BUCKET = 'your-bucket-125xxxxxxx'; export const COS_REGION = 'ap-shanghai'; export const COS_PUBLIC_BASE = 'https://your-bucket-125xxxxxxx.cos.ap-shanghai.myqcloud.com'; export const COS_KEY_PREFIX = 'uploads/'; ``` 说明: - COS_PUBLIC_BASE:若桶或 CDN 具有公网访问,使用对应域名(建议 HTTPS)。若为私有桶,可忽略该项(前端不能直接拼公开 URL)。 - `buildCosKey` 会依据日期和随机串生成不重复 Key,可通过 `prefix`/`userId` 定制目录。 ## 后端接口约定:/api/users/cos-token - 方法:`GET` - 鉴权:建议要求登录态,后端根据用户权限签发最小权限临时凭证。 - 响应体示例(关键字段): ```json { "credentials": { "tmpSecretId": "TMPID...", "tmpSecretKey": "TMPKEY...", "sessionToken": "SESSION_TOKEN..." }, "startTime": 1730000000, "expiredTime": 1730001800 } ``` - 最小权限策略建议(示例,后端侧): ```json { "version": "2.0", "statement": [ { "action": [ "name/cos:PutObject", "name/cos:InitiateMultipartUpload", "name/cos:UploadPart", "name/cos:CompleteMultipartUpload" ], "effect": "allow", "resource": [ "qcs::cos:ap-shanghai:uid/125xxxxxxx:your-bucket-125xxxxxxx/*" ] } ] } ``` 注意:对上传路径进行前缀限制(如 `uploads/*`)能进一步收敛权限范围。 ## 前端用法一:Hook(推荐) 在页面/组件内使用 `useCosUpload`,支持进度、取消与重试。 ```tsx import { useCosUpload } from '@/hooks/useCosUpload'; import * as ImagePicker from 'expo-image-picker'; export default function Uploader() { const { upload, progress, uploading, cancel } = useCosUpload({ prefix: 'images/', userId: 'uid-123' }); const pickAndUpload = async () => { const res = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images }); if (res.canceled || !res.assets?.[0]) return; const asset = res.assets[0]; const result = await upload({ uri: asset.uri, name: asset.fileName, type: asset.mimeType }); console.log('uploaded:', result); // { key, url } }; return null; } ``` 说明: - `progress` 范围 0-1;`uploading` 指示进行中;可随时 `cancel()`。 - 未开启公网访问时,`url` 可能为空(需服务端签名下载)。 ## 前端用法二:直接调用服务 `services/cos.ts` 提供基础方法: ```ts import { uploadWithRetry } from '@/services/cos'; const { key, etag } = await uploadWithRetry({ key: 'uploads/demo.png', body: blob, contentType: 'image/png' }); ``` - 进度回调:`onProgress: ({ percent }) => { ... }` - 取消:传入 `signal: AbortSignal` - 重试:`maxRetries`/`backoffMs` 配置指数退避 ## URL 构建 若为公网可访问场景,使用: ```ts import { buildPublicUrl } from '@/constants/Cos'; const url = buildPublicUrl(key); ``` 若为私有桶,请改走服务端生成临时下载链接或代理下载。 ## 大文件与分片上传 当前默认使用 `putObject`。若需大文件/断点续传,建议切换 SDK 的分片接口 `sliceUploadFile`: ```ts const taskId = cos.sliceUploadFile({ Bucket: COS_BUCKET, Region: COS_REGION, Key: key, Body: fileOrBlob, onProgress(progress) { /* ... */ } }, (err, data) => { /* ... */ }); ``` 如需切换,可在 `services/cos.ts` 中将 `putObject` 替换为 `sliceUploadFile`,并保留同样的进度、取消与重试包装。 ## CORS 与浏览器(如使用 Web) - COS 控制台需配置 CORS,允许来源域名、方法(PUT/POST/OPTIONS)、请求头(含 `Authorization`、`x-cos-*` 等)、暴露头。 - 若通过自有域名/网关代理,也需在代理层正确透传 CORS 与签名头。 ## 安全与最佳实践 - 永久密钥仅存在服务端,通过 `/api/users/cos-token` 颁发短期凭证。 - 严控临时凭证权限与有效期,并限制上传前缀。 - 前端仅持有短期凭证;错误重试次数受限,避免暴力重放。 ## 常见问题排查 - 403/签名失败:确认服务端时间同步、`region/bucket`/`resource` 配置正确;临时密钥未过期。 - CORS 失败:检查 COS 跨域规则;确认需要的请求头(含 `Authorization`)均已放行。 - URL 访问 403:私有桶下请改用服务端签名下载;或为指定前缀开启公共读(谨慎)。 - MIME/ContentType:从 ImagePicker 取 `mimeType`,或按扩展名兜底设置。 ## 测试清单 - 小图上传成功且进度递增、可取消。 - 取消后不再继续发片段;重试如期生效。 - 返回的 `key` 能通过 `buildPublicUrl` 拼出可访问地址(公网桶)。 - 临时密钥过期后,能够重新获取并继续上传。 ## 变更点速记 - 依赖:`cos-js-sdk-v5` - 新增:`constants/Cos.ts`、`services/cos.ts`、`hooks/useCosUpload.ts` - 接口:`GET /api/users/cos-token`(返回临时凭证) --- 如需我将某页面接入示例按钮或改造为分片上传,请在需求中指明目标文件与交互期望。