const HEIC_MIME_TYPES = [ 'image/heic', 'image/heif', 'image/heic-sequence', 'image/heif-sequence', ] export function isHeicOrHeif(file: File): boolean { const name = file.name.toLowerCase() const type = (file.type || '').toLowerCase() return ( name.endsWith('.heic') || name.endsWith('.heif') || HEIC_MIME_TYPES.includes(type) ) } function replaceExtension(fileName: string, extension: string): string { const lastDot = fileName.lastIndexOf('.') if (lastDot <= 0) return `${fileName}.${extension}` return `${fileName.slice(0, lastDot)}.${extension}` } export async function convertHeicHeifToWebp(file: File): Promise { const type = (file.type || '').toLowerCase() // If the blob already has a browser-readable image MIME (and it's not a HEIC MIME), // skip conversion and return the original file. if (type && type.startsWith('image/') && !HEIC_MIME_TYPES.includes(type)) { return file } const { default: heic2any } = await import('heic2any') try { const result = await heic2any({ blob: file, toType: 'image/webp', quality: 0.92, }) const blob = Array.isArray(result) ? result[0] : result if (!(blob instanceof Blob)) { throw new Error('HEIC/HEIF 转换失败') } const targetFileName = replaceExtension(file.name, 'webp') return new File([blob], targetFileName, { type: 'image/webp', lastModified: Date.now(), }) } catch (err: any) { // heic2any may throw when the provided blob is already browser-readable // (e.g. it contains image/jpeg despite .heic extension). In that case, // treat it as already-converted and return the original file. const msg = String(err?.message || '') if (err && (err.code === 1 || /already browser readable/i.test(msg))) { return file } throw err instanceof Error ? err : new Error('HEIC/HEIF 转换失败') } }