// 对象存储签名工具:浏览器直传用查询串签名(浏览器无法设置 Date 请求头) import crypto from "node:crypto"; const REGION = process.env.OSS_REGION ?? "oss-cn-hangzhou"; const BUCKET = process.env.OSS_BUCKET ?? ""; const ID = process.env.OSS_ACCESS_KEY_ID ?? ""; const SECRET = process.env.OSS_ACCESS_KEY_SECRET ?? ""; const HOST = `${BUCKET}.${REGION}.aliyuncs.com`; // 生成带签名的临时访问地址 function signUrl(method: string, key: string, seconds: number, contentType = "") { const expires = Math.floor(Date.now() / 1000) + seconds; const resource = `/${BUCKET}/${key}`; const str = `${method}\n\n${contentType}\n${expires}\n${resource}`; const signature = crypto.createHmac("sha1", SECRET).update(str).digest("base64"); const qs = new URLSearchParams({ OSSAccessKeyId: ID, Expires: String(expires), Signature: signature, }); return `https://${HOST}/${encodeURIComponent(key)}?${qs}`; } // 直传地址:浏览器收到后直接 fetch PUT,有效期 10 分钟 export function uploadUrl(key: string, contentType: string) { return signUrl("PUT", key, 600, contentType); } // 展示地址:签名 GET,有效期 5 分钟,用完由页面刷新重新签发 export function viewUrl(key: string) { return signUrl("GET", key, 300); } // 照片键名:photos/孩子ID/日期/随机16进制.jpg export function photoKey(childId: number, date: string) { return `photos/${childId}/${date}/${crypto.randomBytes(8).toString("hex")}.jpg`; }