81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
|
||
|
||
const s3Client = new S3Client({
|
||
endpoint: process.env.S3_ENDPOINT,
|
||
region: process.env.S3_REGION || 'us-east-1',
|
||
credentials: {
|
||
accessKeyId: process.env.S3_ACCESS_KEY_ID!,
|
||
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
|
||
},
|
||
forcePathStyle: true, // MinIO requires path-style
|
||
requestHandler: {
|
||
// 增加超时时间,提高上传稳定性
|
||
requestTimeout: 300000, // 5分钟超时
|
||
connectionTimeout: 30000, // 30秒连接超时
|
||
},
|
||
maxAttempts: 10, // 失败重试10次,确保上传成功
|
||
})
|
||
|
||
const buildKeyPrefix = () => {
|
||
const sanitize = (value?: string | null) => {
|
||
if (!value) return null
|
||
return value.replace(/^\/+|\/+$/g, '')
|
||
}
|
||
|
||
const segments = [
|
||
sanitize(process.env.S3_ENV_PREFIX),
|
||
sanitize(process.env.S3_FILE_PATH),
|
||
].filter(Boolean)
|
||
|
||
if (!segments.length) return ''
|
||
return `${segments.join('/')}/`
|
||
}
|
||
|
||
export async function uploadToS3(
|
||
file: Buffer,
|
||
fileName: string,
|
||
contentType: string
|
||
): Promise<string> {
|
||
const keyPrefix = buildKeyPrefix()
|
||
const key = `${keyPrefix}${fileName}`
|
||
|
||
const command = new PutObjectCommand({
|
||
Bucket: process.env.S3_BUCKET!,
|
||
Key: key,
|
||
Body: file,
|
||
ContentType: contentType,
|
||
ACL: 'public-read',
|
||
})
|
||
|
||
try {
|
||
await s3Client.send(command)
|
||
return `${process.env.S3_PUBLIC_DOMAIN}/${key}`
|
||
} catch (error) {
|
||
// S3上传失败会自动重试(maxAttempts: 10),这里只记录日志
|
||
console.error('[S3] Upload error (after all retries):', error)
|
||
// 重新抛出错误,让上层处理
|
||
throw new Error('S3上传失败,请稍后重试')
|
||
}
|
||
}
|
||
|
||
export async function deleteFromS3(fileUrl: string): Promise<void> {
|
||
try {
|
||
// Extract key from URL
|
||
const url = new URL(fileUrl)
|
||
const key = url.pathname.slice(1) // Remove leading slash
|
||
|
||
const command = new DeleteObjectCommand({
|
||
Bucket: process.env.S3_BUCKET!,
|
||
Key: key,
|
||
})
|
||
|
||
await s3Client.send(command)
|
||
} catch (error) {
|
||
console.error('Error deleting from S3:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
export { s3Client }
|
||
|