开发者的 HEIC 转 JPG 方案:云 API、自建与浏览器端
只要你的应用接收 iPhone 拍的照片,就会收到 HEIC 文件,而大多数浏览器显示不了它。把 HEIC 转成 JPG 有三类做法:交给云端 API、在自己的服务器上跑解码库、在用户浏览器里解码后只上传 JPG。三者最大的区别是原图会去哪里。下面依据各项目自己的文档逐一对比,核对日期为 2026 年 10 月 31 日。
三类方案一览
| 方案 | HEIC 在哪里解码 | 例子 | 要确认的事 |
|---|---|---|---|
| 云端 API | 服务商的服务器 | CloudConvert API、Cloudinary | 文件会上传到服务商并由其存储 |
| 自建服务 | 你自己的服务器 | libheif、ImageMagick、libvips / sharp | 编译版本必须带 HEVC 解码器 |
| 浏览器端 | 用户自己的设备 | heic-to、heic2any、libheif-js | 库的体积、用户设备的 CPU 与内存、元数据 |
云端 API
**CloudConvert API。**处理以「任务(job)」为单位,一个任务由几步组成:导入文件、转换、导出结果。CloudConvert 公开的格式列表里,heic 转 jpg 使用 imagemagick 引擎,每次消耗 1 个 credit。最简单的任务用 POST https://api.cloudconvert.com/v2/jobs 提交:
{
"tasks": {
"import-my-file": { "operation": "import/url", "url": "https://example.com/photo.heic" },
"convert-my-file": { "operation": "convert", "input": "import-my-file", "input_format": "heic", "output_format": "jpg" },
"export-my-file": { "operation": "export/url", "input": "convert-my-file" }
}
}
import/upload 这一步可以让用户把文件直接传给 CloudConvert,不经过你的服务器。它的隐私政策写明,文件会传输到 CloudConvert 的服务器并临时存储;API 文档写明,任务结束 24 小时后自动删除。处理区域可以固定为 eu-central(德国)或 us-east(美国弗吉尼亚)。
**Cloudinary。**Cloudinary 的格式表把 HEIF(.heif、.heic)列为支持上传、支持变换。做法是先把原图上传到你的 Cloudinary 账户,再按另一种格式请求它:文档说需要固定格式时用 f_jpg,Cloudinary 会把图片转换为请求的格式再交付。
无论用哪家,原图都会离开你的基础设施,由服务商存储。本文不写价格:两家都按套餐或 credit 计费,我们没有核实当前价格。
自建服务
libheif 是几乎所有处理 HEIC 的开源应用都在用的 HEIF 库。它的 README 写明:HEIC 解码默认用 libde265,编码用 x265,而 x265 是 GPL 许可;libheif 本身是 LGPL。自带的 heif-dec 命令会把 HEIF 文件里的每一张图都转换出来:
heif-dec photo.heic photo.jpg
Python(pillow_heif、pyheif)、Go、Rust、.NET、Node.js 都有绑定。README 还提到,仅 2026 年就调查并修复了 89 个安全公告,所以如果要解码用户上传的文件,记得及时更新。
ImageMagick 能读写 HEIC,但格式表注明 HEIC 需要 libheif 委托库(原文 Requires libheif delegate library)。先用 magick -list format 确认你的版本支持,再转换:
magick photo.heic -quality 90 photo.jpg
**Node.js 的 sharp(基于 libvips)。**sharp 的预编译版本支持 JPEG、PNG、Ultra HDR、WebP、AVIF、TIFF、GIF 和 SVG(仅输入)。对 iPhone 照片这种 HEVC 压缩的 HEIC,sharp 在 heif() 输出选项的文档里要求使用全局安装、编译时带 libheif、libde265 和 x265 的 libvips。我们在 Mac 上用预编译的 sharp 0.35.4 试了一张 1200 万像素的 iPhone 7 照片:metadata() 能读出 heif 和 hevc,但转 JPEG 时报错 heif: Decoder plugin generated an error。也就是说,元数据检查通过的文件,照样可能解码失败。
只在 Mac 上,系统自带的 sips 命令不用安装任何东西就能转换。在 macOS 26 上,它把同一张照片转成了 4032×3024 的 JPG,并保留了 Exif:
sips -s format jpeg -s formatOptions 90 photo.heic --out photo.jpg
heif-dec 和 magick 我们没有实际运行,因为测试机上没有安装;命令按官方文档写成。
浏览器端
在浏览器里解码,服务器自始至终只收到 JPG。代价是要加载一个很大的脚本,转换也占用用户设备的 CPU 和内存。
| 库 | 许可证(npm) | 最新版本 | README 中的要点 |
|---|---|---|---|
| heic-to | LGPL-3.0 | 1.6.7,2026-10-27 | 跟进 libheif 新版本;Web Worker 里用 heic-to/next,严格 CSP 下用 heic-to/csp |
| heic2any | MIT | 0.0.4,2023-03-29 | 只能在浏览器里用;不复制元数据 |
| libheif-js | LGPL-3.0 | 1.23.5,2026-10-04 | 用 Emscripten 编译的 libheif,可用于 Node.js 和浏览器;底层解码 API |
heic-to 的 README 给出的调用方式:
import { heicTo } from "heic-to"
const jpeg = await heicTo({ blob: file, type: "image/jpeg", quality: 0.9 })
我们在 Chromium 里用 heic-to 1.5.2 对那张 iPhone 7 照片做了同样的调用,得到一张 4032×3024、1,494,239 字节的 JPEG,里面没有 Exif。需要拍摄时间或方向信息的话,得自己写回去。
heicly 是怎么做的
heicly 是一个网页,不提供 API。它在 Web Worker 里调用 heic-to 1.5.2:先把照片解码成 ImageBitmap,再画到 OffscreenCanvas 上,这一步同时纠正方向,并把超过 16,777,216 像素的大图按比例缩小;然后编码成 JPG,默认质量 90。最近一次转换的画布会缓存下来,拖动质量滑块时只重新编码,不再解码。拍摄时间和相机信息会写回 JPG,拍摄位置只有用户打开选项后才保留。
怎么选
- **已经在用媒体云服务:**云端 API 最省事,前提是你的隐私政策允许把用户照片上传给该服务商。
- **文件只能留在自己的服务器:**用带 HEVC 解码的 libheif、ImageMagick 或 libvips,并保持更新。
- **希望原图留在用户设备上:**在浏览器里转换,只上传 JPG。记得用大照片在低端手机上测试。
先看看浏览器端的效果
想先看看浏览器端转换的效果?直接试下面的转换器。它在本页运行,照片不会上传。
将 HEIC 或 HEIF 照片拖到这里
或者
你的文件不会离开这台设备——转换全程在浏览器里完成。转换过一次后,断网也能用。
可一次选择多张 HEIC 或 HEIF 照片。超大照片可能会被缩小以适应浏览器限制;在手机上转换大量照片时,建议分批进行。
注意:刷新或关闭页面会清除当前任务和结果。
想了解格式本身,可以看 HEIC 和 JPG 的区别 和 照片格式指南。
来源
2026 年 10 月 31 日核对:CloudConvert 的 API 简介、Jobs、导入文件、格式列表与隐私政策;Cloudinary 的支持格式、图片优化与上传 API;libheif README;ImageMagick 的格式表与命令行选项;sharp 的安装说明与输出选项;npm 上的 heic-to、heic2any 与 libheif-js。