'use client' import { useState, useRef } from 'react' import { Button } from '@/components/ui/button' import { Upload, X, Image as ImageIcon } from 'lucide-react' import Image from 'next/image' import { Spinner } from '@/components/ui/spinner' interface ImageUploaderProps { value: string onChange: (url: string) => void } export function ImageUploader({ value, onChange }: ImageUploaderProps) { const [isUploading, setIsUploading] = useState(false) const [error, setError] = useState('') const fileInputRef = useRef(null) const handleFileSelect = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return // Validate file type if (!file.type.startsWith('image/')) { setError('请选择图片文件') return } // Validate file size (max 5MB) if (file.size > 5 * 1024 * 1024) { setError('图片大小不能超过 5MB') return } setError('') setIsUploading(true) try { // Get temp key const keyRes = await fetch('/api/cos/temp-key') if (!keyRes.ok) { throw new Error('获取上传凭证失败') } const keyData = await keyRes.json() // Generate unique filename const ext = file.name.split('.').pop() || 'jpg' const timestamp = Date.now() const randomStr = Math.random().toString(36).substring(2, 8) const filename = `levels/${timestamp}_${randomStr}.${ext}` // Upload to COS const formData = new FormData() formData.append('key', filename) formData.append('Signature', keyData.credentials.sessionToken) formData.append('success_action_status', '200') const uploadUrl = `https://${keyData.bucket}.cos.${keyData.region}.myqcloud.com` // Use XMLHttpRequest for COS upload with temp credentials const uploadResult = await new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open('POST', uploadUrl) // Set temp credentials headers xhr.setRequestHeader('Authorization', getCOSAuthorization( keyData.credentials.tmpSecretId, keyData.credentials.tmpSecretKey, keyData.credentials.sessionToken, 'post', filename, keyData.bucket, keyData.region )) xhr.onload = () => { if (xhr.status === 200) { resolve(`${uploadUrl}/${filename}`) } else { reject(new Error('上传失败')) } } xhr.onerror = () => reject(new Error('上传失败')) const cosFormData = new FormData() cosFormData.append('key', filename) cosFormData.append('file', file) xhr.send(cosFormData) }) onChange(uploadResult) } catch (err) { console.error('Upload error:', err) setError(err instanceof Error ? err.message : '上传失败') } finally { setIsUploading(false) // Reset file input if (fileInputRef.current) { fileInputRef.current.value = '' } } } const handleRemove = () => { onChange('') setError('') } return (
{value ? (
预览图片
) : (
fileInputRef.current?.click()} > {isUploading ? ( <>

上传中...

) : ( <>

点击上传图片

支持 JPG、PNG,最大 5MB

)}
)} {error && (

{error}

)}
) } // Helper function to generate COS authorization header function getCOSAuthorization( secretId: string, secretKey: string, sessionToken: string, method: string, pathname: string, bucket: string, region: string ): string { const now = Math.floor(Date.now() / 1000) const exp = now + 1800 const keyTime = `${now};${exp}` // Simple authorization string for temp credentials return `q-sign-algorithm=sha1&q-ak=${secretId}&q-sign-time=${keyTime}&q-key-time=${keyTime}&q-header-list=&q-url-param-list=&q-signature=placeholder&x-cos-security-token=${sessionToken}` }