You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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的服务端兼容性,适合大文件或统一处理场景:

  1. 安装依赖:
npm install sharp
  1. 创建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}` })
}
  1. 前端上传代码:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:20:26