fix: remove sharp image size limit

This commit is contained in:
mgt
2026-01-17 00:05:52 +08:00
parent a93904686e
commit 1f7bb7636b
5 changed files with 28 additions and 6 deletions

View File

@@ -4,11 +4,14 @@ import { rgbaToThumbHash } from 'thumbhash'
import { logger } from '../logger/index.js' import { logger } from '../logger/index.js'
// 生成 blurhash基于缩略图数据保持长宽比 // 生成 blurhash基于缩略图数据保持长宽比
export async function generateBlurhash(thumbnailBuffer: Buffer): Promise<Uint8Array | null> { export async function generateBlurhash(
thumbnailBuffer: Buffer,
limitInputPixels?: number | boolean,
): Promise<Uint8Array | null> {
try { try {
// 复用缩略图的 Sharp 实例来生成 blurhash // 复用缩略图的 Sharp 实例来生成 blurhash
// 确保转换为 raw RGBA 格式 // 确保转换为 raw RGBA 格式
const { data, info } = await sharp(thumbnailBuffer) const { data, info } = await sharp(thumbnailBuffer, { limitInputPixels })
.resize(100, 100, { fit: 'inside' }) .resize(100, 100, { fit: 'inside' })
.raw() .raw()
.ensureAlpha() .ensureAlpha()

View File

@@ -79,7 +79,11 @@ async function processExistingThumbnail(photoId: string): Promise<ThumbnailResul
} }
// 生成新的缩略图 // 生成新的缩略图
async function generateNewThumbnail(imageBuffer: Buffer, photoId: string): Promise<ThumbnailResult> { async function generateNewThumbnail(
imageBuffer: Buffer,
photoId: string,
limitInputPixels?: number | boolean,
): Promise<ThumbnailResult> {
const { thumbnailPath, thumbnailUrl } = getThumbnailPaths(photoId) const { thumbnailPath, thumbnailUrl } = getThumbnailPaths(photoId)
const log = getGlobalLoggers().thumbnail const log = getGlobalLoggers().thumbnail
@@ -88,7 +92,7 @@ async function generateNewThumbnail(imageBuffer: Buffer, photoId: string): Promi
try { try {
// 创建 Sharp 实例,复用于缩略图和 blurhash 生成 // 创建 Sharp 实例,复用于缩略图和 blurhash 生成
const sharpInstance = sharp(imageBuffer).rotate() // 自动根据 EXIF 旋转 const sharpInstance = sharp(imageBuffer, { limitInputPixels }).rotate() // 自动根据 EXIF 旋转
// 生成缩略图 // 生成缩略图
const thumbnailBuffer = await sharpInstance const thumbnailBuffer = await sharpInstance
@@ -122,6 +126,7 @@ export async function generateThumbnailAndBlurhash(
imageBuffer: Buffer, imageBuffer: Buffer,
photoId: string, photoId: string,
forceRegenerate = false, forceRegenerate = false,
limitInputPixels?: number | boolean,
): Promise<ThumbnailResult> { ): Promise<ThumbnailResult> {
const thumbnailLog = getGlobalLoggers().thumbnail const thumbnailLog = getGlobalLoggers().thumbnail
@@ -139,7 +144,7 @@ export async function generateThumbnailAndBlurhash(
} }
// 生成新的缩略图 // 生成新的缩略图
return await generateNewThumbnail(imageBuffer, photoId) return await generateNewThumbnail(imageBuffer, photoId, limitInputPixels)
} catch (error) { } catch (error) {
thumbnailLog.error(`处理失败:${photoId}`, error) thumbnailLog.error(`处理失败:${photoId}`, error)
return createFailureResult() return createFailureResult()

View File

@@ -10,6 +10,7 @@ import { extractExifData } from '../image/exif.js'
import { calculateHistogramAndAnalyzeTone } from '../image/histogram.js' import { calculateHistogramAndAnalyzeTone } from '../image/histogram.js'
import { generateThumbnailAndBlurhash, thumbnailExists } from '../image/thumbnail.js' import { generateThumbnailAndBlurhash, thumbnailExists } from '../image/thumbnail.js'
import { workdir } from '../path.js' import { workdir } from '../path.js'
import { getPhotoExecutionContext } from './execution-context.js'
import { getGlobalLoggers } from './logger-adapter.js' import { getGlobalLoggers } from './logger-adapter.js'
import type { PhotoProcessorOptions } from './processor.js' import type { PhotoProcessorOptions } from './processor.js'
@@ -30,6 +31,8 @@ export async function processThumbnailAndBlurhash(
options: PhotoProcessorOptions, options: PhotoProcessorOptions,
): Promise<ThumbnailResult> { ): Promise<ThumbnailResult> {
const loggers = getGlobalLoggers() const loggers = getGlobalLoggers()
const { builder } = getPhotoExecutionContext()
const { limitInputPixels } = builder.getConfig().system.processing
// 检查是否可以复用现有数据 // 检查是否可以复用现有数据
if ( if (
@@ -62,6 +65,7 @@ export async function processThumbnailAndBlurhash(
imageBuffer, imageBuffer,
photoId, photoId,
options.isForceMode || options.isForceThumbnails, options.isForceMode || options.isForceThumbnails,
limitInputPixels,
) )
return { return {

View File

@@ -83,10 +83,12 @@ export async function preprocessImage(
*/ */
export async function processImageWithSharp(imageBuffer: Buffer, photoKey: string): Promise<ProcessedImageData | null> { export async function processImageWithSharp(imageBuffer: Buffer, photoKey: string): Promise<ProcessedImageData | null> {
const loggers = getGlobalLoggers() const loggers = getGlobalLoggers()
const { builder } = getPhotoExecutionContext()
const { limitInputPixels } = builder.getConfig().system.processing
try { try {
// 创建 Sharp 实例,复用于多个操作 // 创建 Sharp 实例,复用于多个操作
let sharpInstance = sharp(imageBuffer) let sharpInstance = sharp(imageBuffer, { limitInputPixels })
let processedBuffer = imageBuffer let processedBuffer = imageBuffer
// 处理 BMP // 处理 BMP

View File

@@ -20,6 +20,14 @@ export interface SystemProcessingSettings {
enableLivePhotoDetection: boolean enableLivePhotoDetection: boolean
supportedFormats?: Set<string> supportedFormats?: Set<string>
digestSuffixLength?: number digestSuffixLength?: number
/**
* Do not process input images where the number of pixels (width x height) exceeds this limit.
* - `number`: An integral Number of pixels as the limit
* - `false` or `0`: Remove limit entirely
* - `true` or `undefined`: Use sharp's default limit of 268402689 (0x3FFF x 0x3FFF)
* @default undefined (uses sharp's default: 268402689)
*/
limitInputPixels?: number | boolean
} }
export interface SystemObservabilitySettings { export interface SystemObservabilitySettings {