gongxiangp2/lib/psd-processor.ts
chenpei 0666edc4a8 feat: Update preview image dimensions to 300x500
- Modify PSD processor to crop long images to top 1200px
- Update image processor to support maxHeight parameter (1000px)
- Change homepage card height from 300px to 500px
- Update upload API to use new image processing parameters
- Support vertical/portrait images better with 300x500 display
2025-10-26 16:15:14 +08:00

144 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { readPsd } from 'ag-psd'
import sharp from 'sharp'
import fs from 'fs/promises'
interface PsdProcessResult {
success: boolean
previewPath?: string
width?: number
height?: number
error?: string
}
/**
* 从PSD文件生成预览图
* @param psdFilePath PSD文件路径
* @param outputPath 输出预览图路径
* @param maxSize 最大尺寸(宽或高),默认800px
* @param maxHeight 最大高度限制,长图裁剪,默认1200px
* @returns 处理结果
*/
export async function generatePsdPreview(
psdFilePath: string,
outputPath: string,
maxSize: number = 800,
maxHeight: number = 1200
): Promise<PsdProcessResult> {
try {
// 读取PSD文件
const psdBuffer = await fs.readFile(psdFilePath)
// 解析PSD
const psd = readPsd(psdBuffer, {
skipLayerImageData: true, // 跳过图层数据,只读取合成图像
skipThumbnail: false
})
if (!psd) {
return {
success: false,
error: 'Failed to parse PSD file'
}
}
// 获取PSD的宽高
const width = psd.width || 0
const height = psd.height || 0
if (width === 0 || height === 0) {
return {
success: false,
error: 'Invalid PSD dimensions'
}
}
// 如果PSD有缩略图,优先使用缩略图
if (psd.imageData) {
const imageData = psd.imageData
const channels = 4 // RGBA
// 将PSD图像数据转换为Buffer
const rawBuffer = Buffer.from(imageData.data)
// 创建Sharp实例
let sharpInstance = sharp(rawBuffer, {
raw: {
width: imageData.width,
height: imageData.height,
channels: channels
}
})
// 如果是长图(高度超过maxHeight),先裁剪顶部区域
if (imageData.height > maxHeight) {
console.log(`[PSD] 检测到长图 ${imageData.width}x${imageData.height},裁剪到顶部${maxHeight}px`)
sharpInstance = sharpInstance.extract({
left: 0,
top: 0,
width: imageData.width,
height: maxHeight
})
}
// 然后进行缩放处理
await sharpInstance
.resize(maxSize, maxSize, {
fit: 'inside',
withoutEnlargement: true
})
.jpeg({
quality: 90,
mozjpeg: true
})
.toFile(outputPath)
return {
success: true,
previewPath: outputPath,
width,
height
}
}
return {
success: false,
error: 'No image data found in PSD file'
}
} catch (error) {
console.error('Error generating PSD preview:', error)
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 检查文件是否为PSD格式
* @param filePath 文件路径
* @returns 是否为PSD文件
*/
export async function isPsdFile(filePath: string): Promise<boolean> {
try {
const buffer = await fs.readFile(filePath, { encoding: null })
// PSD文件签名:8BPS
return buffer.length > 4 &&
buffer[0] === 0x38 && // '8'
buffer[1] === 0x42 && // 'B'
buffer[2] === 0x50 && // 'P'
buffer[3] === 0x53 // 'S'
} catch {
return false
}
}
/**
* 从文件扩展名判断是否为PSD
* @param filename 文件名
* @returns 是否为PSD文件
*/
export function hasPsdExtension(filename: string): boolean {
return filename.toLowerCase().endsWith('.psd')
}