如何在React/Next.js中将HEIC格式转换为JPG格式?
HEIC转JPG方案求助
HEIC是苹果专属格式,用于存储iOS相机拍摄的高分辨率图片。但我需要在后端存储JPG格式,因为HEIC在多数浏览器中无法正常显示,甚至包括Safari。
已尝试的方案
方案1:使用heicConvert
尝试了以下转换代码:
const buffer = Buffer.from(await file.arrayBuffer()) const d = heicConvert({ buffer, format: 'JPEG' }) const imgBase64 = btoa( d.reduce((data, byte) => `${data}${String.fromCharCode(byte)}`, '') )
但在Next.js中不兼容,编译报错:
Failed to compile. ./node_modules/libheif-js/libheif/libheif.js Module not found: Can't resolve 'fs' in '/Users/janoskukoda/Workspace/tikex/portal/team/node_modules/libheif-js/libheif'
方案2:使用sharp
尝试代码如下:
export default uploadImage const buffer = await file.arrayBuffer() const image = sharp(buffer) const metadata = await image.metadata() if (metadata.format === 'heic') { // 转换为JPG const jpgBuffer = await image.jpeg().toBuffer() // 编码为base64字符串 const imgBase64 = btoa( jpgBuffer.reduce((data, byte) => `${data}${String.fromCharCode(byte)}`, '') ) }
无法编译,因为sharp不推荐在客户端环境使用。
方案3:使用blueimp-load-image
尝试代码:
import loadImage from 'blueimp-load-image' convertedImage = await new Promise((resolve, reject) => { loadImage( propsAndFile.file, resolve, { orientation: true, canvas: true }, reject ) })
可行解决方案
方案A:客户端转JPG后上传(完善方案3)
基于blueimp-load-image将HEIC转为JPG Blob后直接上传,纯客户端处理无需服务端特殊依赖:
import loadImage from 'blueimp-load-image' async function convertHeicToJpg(file) { return new Promise((resolve, reject) => { loadImage( file, (canvas) => { // 将Canvas转为JPG格式Blob canvas.toBlob( (blob) => { // 包装为File对象方便上传 const jpgFile = new File([blob], `${file.name.split('.')[0]}.jpg`, { type: 'image/jpeg' }) resolve(jpgFile) }, 'image/jpeg', 0.9 // 图片质量(0-1区间) ) }, { orientation: true, canvas: true }, reject ) }) } // 前端使用示例:监听文件输入 const fileInput = document.querySelector('input[type="file"]') fileInput.addEventListener('change', async (e) => { const file = e.target.files[0] if (file?.type === 'image/heic') { const jpgFile = await convertHeicToJpg(file) // 上传到后端接口 const formData = new FormData() formData.append('image', jpgFile) await fetch('/api/upload', { method: 'POST', body: formData }) } })
方案B:Serverless API转换(Next.js路由为例)
将转换逻辑放在服务端,利用sharp的服务端兼容性,适合大文件或统一处理场景:
- 安装依赖:
npm install sharp
- 创建
pages/api/upload.js路由:
import sharp from 'sharp' import { writeFile } from 'fs/promises' import { join } from 'path' import FormData from 'form-data' export const config = { api: { bodyParser: false, }, } export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: '仅支持POST请求' }) } // 解析FormData const formData = await new Promise((resolve, reject) => { const form = new FormData() req.pipe(form) form.on('end', () => resolve(form)) form.on('error', reject) }) const file = formData.get('image') if (!file || file.type !== 'image/heic') { return res.status(400).json({ message: '请上传合法的HEIC文件' }) } // HEIC转JPG const buffer = await file.arrayBuffer() const jpgBuffer = await sharp(buffer) .jpeg({ quality: 90 }) .toBuffer() // 存储文件(示例为本地存储,可替换为云存储) const fileName = `${Date.now()}.jpg` const savePath = join(process.cwd(), 'public', 'uploads', fileName) await writeFile(savePath, jpgBuffer) res.status(200).json({ message: '转换上传成功', filePath: `/uploads/${fileName}` }) }
- 前端上传代码:
const fileInput = document.querySelector('input[type="file"]') fileInput.addEventListener('change', async (e) => { const file = e.target.files[0] if (file) { const formData = new FormData() formData.append('image', file) await fetch('/api/upload', { method: 'POST', body: formData }) } })
方案C:兼容heicConvert的服务端配置
若坚持使用heicConvert,可在Next.js中配置忽略fs模块解析,仅在服务端执行转换:
修改next.config.js:
module.exports = { webpack: (config) => { config.resolve.fallback = { fs: false } return config }, }
之后将方案1的代码移至API路由中执行,避免在客户端调用。
内容的提问来源于stack exchange,提问作者János
相关产品推荐
相关产品推荐

