import OSS from 'ali-oss'; let client: OSS | null = null; function getClient(): OSS { if (client) return client; const region = process.env.OSS_REGION || 'oss-cn-shanghai'; const accessKeyId = process.env.OSS_ACCESS_KEY_ID || ''; const accessKeySecret = process.env.OSS_ACCESS_KEY_SECRET || ''; const bucket = process.env.OSS_BUCKET || ''; if (!accessKeyId || !accessKeySecret || !bucket) { throw new Error('OSS 未配置:OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET / OSS_BUCKET'); } client = new OSS({ region, accessKeyId, accessKeySecret, bucket, secure: true }); return client; } function safeExt(filename: string, fallback = 'png'): string { const m = /\.([a-zA-Z0-9]{1,8})$/.exec(filename); return m ? m[1].toLowerCase() : fallback; } function randId(len = 8): string { return Math.random().toString(36).slice(2, 2 + len); } /** 上传 buffer 到 OSS,返回公开访问的 URL */ export async function uploadFeedbackImage(filename: string, buf: Buffer, mimetype: string): Promise { const c = getClient(); const baseDir = (process.env.OSS_BASE_DIR || '/dos').replace(/^\/+|\/+$/g, ''); const ymd = new Date().toISOString().slice(0, 10); const key = `${baseDir}/feedback/${ymd}/${Date.now().toString(36)}-${randId()}.${safeExt(filename, mimetype.split('/')[1] || 'png')}`; await c.put(key, buf, { headers: { 'Content-Type': mimetype, 'x-oss-object-acl': 'public-read' }, }); const host = (process.env.OSS_HOST || `https://${process.env.OSS_BUCKET}.${process.env.OSS_ENDPOINT}/`).replace(/\/+$/, '/'); return host + key; }