- Add psd-processor library to auto-generate previews from PSD files - Update upload API to detect PSD files and generate previews automatically - Make preview image optional for PSD file uploads - Update UploadModal UI to show PSD files don't require manual preview upload - Install ag-psd package for PSD processing
128 lines
2.9 KiB
TypeScript
128 lines
2.9 KiB
TypeScript
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
|
||
* @returns 处理结果
|
||
*/
|
||
export async function generatePsdPreview(
|
||
psdFilePath: string,
|
||
outputPath: string,
|
||
maxSize: number = 800
|
||
): 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处理图像
|
||
await sharp(rawBuffer, {
|
||
raw: {
|
||
width: imageData.width,
|
||
height: imageData.height,
|
||
channels: channels
|
||
}
|
||
})
|
||
.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')
|
||
}
|
||
|