mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 22:48:17 +00:00
feat(docker): optimize Dockerfile and enhance EXIF data extraction
- Updated Dockerfile to use Alpine base image for reduced size and added necessary runtime dependencies including exiftool. - Refactored EXIF data extraction logic to utilize exiftool with improved error handling and logging. - Simplified metadata handling by removing unnecessary checks and ensuring consistent extraction of EXIF data. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
# Use Alpine base image for smaller size
|
||||
FROM node:lts-alpine AS builder
|
||||
ENV PNPM_HOME=/pnpm
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
@@ -31,10 +32,22 @@ WORKDIR /app
|
||||
|
||||
# Runtime deps for image processing:
|
||||
# - perl: required by exiftool-vendored on Linux
|
||||
# - perl-image-exiftool: provides the exiftool binary (optional but useful)
|
||||
# - perl-image-exiftool: provides the exiftool binary
|
||||
# - vips: runtime library for sharp
|
||||
# - libheif: enable HEIF/HEIC support in libvips when available
|
||||
RUN apk add --no-cache perl perl-image-exiftool vips libheif
|
||||
RUN apk add --no-cache \
|
||||
perl \
|
||||
exiftool \
|
||||
perl-image-exiftool \
|
||||
vips \
|
||||
libheif \
|
||||
libc6-compat \
|
||||
gcompat \
|
||||
&& EXIFTOOL_BIN="$(command -v exiftool)" \
|
||||
&& echo "Using exiftool binary at ${EXIFTOOL_BIN}" \
|
||||
&& "${EXIFTOOL_BIN}" -ver \
|
||||
&& ln -sf "${EXIFTOOL_BIN}" /usr/local/bin/exiftool
|
||||
ENV EXIFTOOL_PATH=/usr/local/bin/exiftool
|
||||
COPY --from=builder /workspace/be/apps/core/dist ./dist
|
||||
COPY --from=builder /workspace/be/packages/db/migrations ./migrations
|
||||
COPY --from=builder /workspace/be/apps/core/docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
|
||||
@@ -3,13 +3,29 @@ import path from 'node:path'
|
||||
|
||||
import { isNil, noop } from 'es-toolkit'
|
||||
import type { ExifDateTime, Tags } from 'exiftool-vendored'
|
||||
import { exiftool } from 'exiftool-vendored'
|
||||
import type { Metadata } from 'sharp'
|
||||
import sharp from 'sharp'
|
||||
import { ExifTool } from 'exiftool-vendored'
|
||||
|
||||
import { getGlobalLoggers } from '../photo/logger-adapter.js'
|
||||
import type { PickedExif } from '../types/photo.js'
|
||||
|
||||
const exiftool = new ExifTool({
|
||||
...(process.env.EXIFTOOL_PATH ? { exiftoolPath: process.env.EXIFTOOL_PATH } : {}),
|
||||
taskTimeoutMillis: 30000,
|
||||
})
|
||||
|
||||
let isExiftoolClosed = false
|
||||
const closeExiftool = () => {
|
||||
if (isExiftoolClosed) {
|
||||
return
|
||||
}
|
||||
isExiftoolClosed = true
|
||||
exiftool.end().catch(noop)
|
||||
}
|
||||
|
||||
process.once('beforeExit', closeExiftool)
|
||||
process.once('SIGINT', closeExiftool)
|
||||
process.once('SIGTERM', closeExiftool)
|
||||
|
||||
// 提取 EXIF 数据
|
||||
export async function extractExifData(imageBuffer: Buffer, originalBuffer?: Buffer): Promise<PickedExif | null> {
|
||||
const log = getGlobalLoggers().exif
|
||||
@@ -18,29 +34,12 @@ export async function extractExifData(imageBuffer: Buffer, originalBuffer?: Buff
|
||||
const tempImagePath = path.resolve('/tmp/image_process', `${crypto.randomUUID()}.jpg`)
|
||||
|
||||
try {
|
||||
log.info('开始提取 EXIF 数据')
|
||||
|
||||
// 首先尝试从处理后的图片中提取 EXIF
|
||||
let metadata = await sharp(imageBuffer).metadata()
|
||||
|
||||
// 如果处理后的图片没有 EXIF 数据,且提供了原始 buffer,尝试从原始图片提取
|
||||
if (!metadata.exif && originalBuffer) {
|
||||
log.info('处理后的图片缺少 EXIF 数据,尝试从原始图片提取')
|
||||
try {
|
||||
metadata = await sharp(originalBuffer).metadata()
|
||||
} catch (error) {
|
||||
log.warn('从原始图片提取 EXIF 失败,可能是不支持的格式:', error)
|
||||
}
|
||||
}
|
||||
|
||||
if (!metadata.exif) {
|
||||
log.warn('未找到 EXIF 数据')
|
||||
return null
|
||||
}
|
||||
|
||||
await writeFile(tempImagePath, originalBuffer || imageBuffer)
|
||||
|
||||
log.info(`开始提取 EXIF 数据, 文件路径: ${tempImagePath}`)
|
||||
const exifData = await exiftool.read(tempImagePath)
|
||||
const result = handleExifData(exifData, metadata)
|
||||
|
||||
const result = handleExifData(exifData)
|
||||
|
||||
if (!exifData) {
|
||||
log.warn('EXIF 数据解析失败')
|
||||
@@ -135,7 +134,7 @@ const pickKeys: Array<keyof Tags | (string & {})> = [
|
||||
'MicroVideoOffset',
|
||||
'MicroVideoPresentationTimestampUs',
|
||||
]
|
||||
function handleExifData(exifData: Tags, metadata: Metadata): PickedExif {
|
||||
function handleExifData(exifData: Tags): PickedExif {
|
||||
const date = {
|
||||
DateTimeOriginal: formatExifDate(exifData.DateTimeOriginal),
|
||||
DateTimeDigitized: formatExifDate(exifData.DateTimeDigitized),
|
||||
@@ -177,8 +176,8 @@ function handleExifData(exifData: Tags, metadata: Metadata): PickedExif {
|
||||
}
|
||||
}
|
||||
const size = {
|
||||
ImageWidth: exifData.ExifImageWidth || metadata.width,
|
||||
ImageHeight: exifData.ExifImageHeight || metadata.height,
|
||||
ImageWidth: exifData.ExifImageWidth,
|
||||
ImageHeight: exifData.ExifImageHeight,
|
||||
}
|
||||
const result: any = structuredClone(exifData)
|
||||
for (const key in result) {
|
||||
|
||||
Reference in New Issue
Block a user