82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
import COS from 'cos-js-sdk-v5'
|
||
import { apiFetch } from '@/lib/api'
|
||
|
||
interface TempKeyResponse {
|
||
credentials: {
|
||
tmpSecretId: string
|
||
tmpSecretKey: string
|
||
sessionToken: string
|
||
}
|
||
startTime: number
|
||
expiredTime: number
|
||
bucket: string
|
||
region: string
|
||
}
|
||
|
||
let cachedKey: TempKeyResponse | null = null
|
||
|
||
async function getTempKey(forceRefresh = false): Promise<TempKeyResponse> {
|
||
const now = Math.floor(Date.now() / 1000)
|
||
// 提前 60 秒失效,避免边界问题
|
||
if (!forceRefresh && cachedKey && cachedKey.expiredTime - 60 > now) {
|
||
return cachedKey
|
||
}
|
||
const res = await apiFetch('/api/cos/temp-key')
|
||
if (!res.ok) {
|
||
throw new Error('获取上传凭证失败')
|
||
}
|
||
const data = (await res.json()) as TempKeyResponse
|
||
cachedKey = data
|
||
return data
|
||
}
|
||
|
||
function buildCosClient(key: TempKeyResponse): COS {
|
||
return new COS({
|
||
getAuthorization: (_options, callback) => {
|
||
callback({
|
||
TmpSecretId: key.credentials.tmpSecretId,
|
||
TmpSecretKey: key.credentials.tmpSecretKey,
|
||
SecurityToken: key.credentials.sessionToken,
|
||
StartTime: key.startTime,
|
||
ExpiredTime: key.expiredTime,
|
||
})
|
||
},
|
||
})
|
||
}
|
||
|
||
function randomFilename(originalName: string): string {
|
||
const ext = originalName.split('.').pop() || 'jpg'
|
||
const timestamp = Date.now()
|
||
const randomStr = Math.random().toString(36).substring(2, 8)
|
||
return `mini_game/images/${timestamp}_${randomStr}.${ext}`
|
||
}
|
||
|
||
/**
|
||
* 上传单个文件到腾讯云 COS,返回可访问的 URL。
|
||
*/
|
||
export async function uploadToCos(file: File | Blob, originalName?: string): Promise<string> {
|
||
const name = originalName || (file as File).name || 'upload.jpg'
|
||
const key = await getTempKey()
|
||
const cos = buildCosClient(key)
|
||
const filename = randomFilename(name)
|
||
|
||
return new Promise<string>((resolve, reject) => {
|
||
cos.putObject(
|
||
{
|
||
Bucket: key.bucket,
|
||
Region: key.region,
|
||
Key: filename,
|
||
Body: file as File,
|
||
},
|
||
(err) => {
|
||
if (err) {
|
||
reject(new Error(err.message || '上传失败'))
|
||
return
|
||
}
|
||
const url = `https://${key.bucket}.cos.${key.region}.myqcloud.com/${filename}`
|
||
resolve(url)
|
||
}
|
||
)
|
||
})
|
||
}
|