Use heic-convert for HEIC-to-JPEG conversion

Sharp's prebuilt libvips lacks HEIF codec support. Replace with
heic-convert (pure JS decoder) for reliable HEIC conversion on all
platforms. Existing HEIC files on disk will be converted on-the-fly
when served via /api/files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
denshooter
2026-02-22 02:01:04 +01:00
parent 6f826c66ea
commit 31dff10636
6 changed files with 74 additions and 6 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import { writeFile, mkdir } from 'fs/promises'
import path from 'path'
import { randomUUID } from 'crypto'
import { getDb } from '@/lib/db'
import sharp from 'sharp'
import { convertHeicToJpeg } from '@/lib/heic'
export const runtime = 'nodejs'
export const maxDuration = 60
@@ -77,7 +77,7 @@ export async function POST(req: NextRequest) {
let finalExt = ext
if (mimeType === 'image/heic' || mimeType === 'image/heif') {
try {
finalBuffer = Buffer.from(await sharp(buffer).jpeg({ quality: 90 }).toBuffer())
finalBuffer = await convertHeicToJpeg(buffer)
finalExt = '.jpg'
} catch {
// Conversion failed — keep original
+2 -1
View File
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
import sharp from 'sharp'
import { convertHeicToJpeg } from '@/lib/heic'
export const runtime = 'nodejs'
@@ -55,7 +56,7 @@ export async function GET(
contentType = 'image/jpeg'
} else {
try {
const converted = await sharp(fs.readFileSync(filePath), { failOn: 'none' }).jpeg({ quality: 90 }).toBuffer()
const converted = await convertHeicToJpeg(fs.readFileSync(filePath))
fs.writeFileSync(jpegPath, converted)
fileToSendPath = jpegPath
ext = '.jpg'
+3 -3
View File
@@ -4,7 +4,7 @@ import path from 'path'
import { cookies } from 'next/headers'
import { createHash, randomUUID } from 'crypto'
import { getDb } from '@/lib/db'
import sharp from 'sharp'
import { convertHeicToJpeg } from '@/lib/heic'
export const runtime = 'nodejs'
export const maxDuration = 60
@@ -112,8 +112,8 @@ export async function POST(req: NextRequest) {
// Convert HEIC/HEIF to JPEG so all browsers can display it
if (mimeType === 'image/heic' || mimeType === 'image/heif') {
try {
const converted = await sharp(buffer).jpeg({ quality: 90 }).toBuffer()
buffer = converted as unknown as Buffer
const converted = await convertHeicToJpeg(buffer)
buffer = converted
ext = '.jpg'
mimeType = 'image/jpeg'
} catch {
+15
View File
@@ -0,0 +1,15 @@
// @ts-expect-error no type declarations available
import heicConvert from 'heic-convert'
/**
* Convert HEIC/HEIF buffer to JPEG.
* Uses heic-convert (pure JS) since sharp's prebuilt libvips lacks HEIF support.
*/
export async function convertHeicToJpeg(buffer: Buffer): Promise<Buffer> {
const result = await heicConvert({
buffer,
format: 'JPEG',
quality: 0.9,
})
return Buffer.from(result)
}