file-adapters
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFile Converter Adapters
文件转换器适配器
This skill provides comprehensive guidance for using packages to process images with resizing, format conversion, watermarking, and pipeline processing.
@rytass/file-converter本技能提供了使用系列包进行图片处理的全面指南,包括图片缩放、格式转换、添加水印和管道处理等功能。
@rytass/file-converterOverview
概述
All adapters implement the interface from , providing a unified API for image processing:
FileConverter@rytass/file-converter| Package | Function | Based On |
|---|---|---|
| Core interfaces and pipeline manager (核心介面與管道管理器) | TypeScript interfaces |
| Image resizing (圖像縮放) | Sharp ^0.34.5 |
| Format conversion (格式轉換) | Sharp ^0.34.5, file-type ^21.1.1 |
| Watermark overlay (浮水印疊加) | Sharp ^0.34.5 |
所有适配器都实现了中的接口,为图片处理提供统一的API:
@rytass/file-converterFileConverter| 包 | 功能 | 基于技术 |
|---|---|---|
| 核心接口与管道管理器 | TypeScript接口 |
| 图像缩放 | Sharp ^0.34.5 |
| 格式转换 | Sharp ^0.34.5, file-type ^21.1.1 |
| 水印叠加 | Sharp ^0.34.5 |
Base Interface (@rytass/file-converter)
基础接口 (@rytass/file-converter)
All adapters share these core concepts:
FileConverter<O> - Main interface for file conversion
typescript
interface FileConverter<O = Record<string, unknown>> {
convert<Buffer>(file: ConvertableFile): Promise<Buffer>;
convert<Readable>(file: ConvertableFile): Promise<Readable>;
}ConvertableFile - Supported input types
typescript
type ConvertableFile = Readable | Buffer;ConverterManager - Pipeline processor for chaining conversions
typescript
class ConverterManager {
constructor(converters: FileConverter[]);
convert<Output>(file: ConvertableFile): Promise<Output>;
}所有适配器共享以下核心概念:
FileConverter<O> - 文件转换主接口
typescript
interface FileConverter<O = Record<string, unknown>> {
convert<Buffer>(file: ConvertableFile): Promise<Buffer>;
convert<Readable>(file: ConvertableFile): Promise<Readable>;
}ConvertableFile - 支持的输入类型
typescript
type ConvertableFile = Readable | Buffer;ConverterManager - 用于链式转换的管道处理器
typescript
class ConverterManager {
constructor(converters: FileConverter[]);
convert<Output>(file: ConvertableFile): Promise<Output>;
}Installation
安装
bash
undefinedbash
undefinedInstall base package
安装基础包
npm install @rytass/file-converter
npm install @rytass/file-converter
Choose the adapters you need
选择需要的适配器
npm install @rytass/file-converter-adapter-image-resizer
npm install @rytass/file-converter-adapter-image-transcoder
npm install @rytass/file-converter-adapter-image-watermark
undefinednpm install @rytass/file-converter-adapter-image-resizer
npm install @rytass/file-converter-adapter-image-transcoder
npm install @rytass/file-converter-adapter-image-watermark
undefinedQuick Start
快速开始
ImageResizer (圖像縮放)
ImageResizer(图像缩放)
Resize images while maintaining or controlling aspect ratio:
typescript
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { readFileSync, writeFileSync } from 'fs';
// Resize to max 800x600, maintaining aspect ratio
const resizer = new ImageResizer({
maxWidth: 800,
maxHeight: 600,
keepAspectRatio: true,
concurrency: 4, // Optional: parallel processing
});
const originalImage = readFileSync('photo.jpg');
const resizedImage = await resizer.convert<Buffer>(originalImage);
writeFileSync('photo-resized.jpg', resizedImage);在保持或控制宽高比的前提下调整图片大小:
typescript
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { readFileSync, writeFileSync } from 'fs';
// 调整至最大800x600,保持宽高比
const resizer = new ImageResizer({
maxWidth: 800,
maxHeight: 600,
keepAspectRatio: true,
concurrency: 4, // 可选:并行处理
});
const originalImage = readFileSync('photo.jpg');
const resizedImage = await resizer.convert<Buffer>(originalImage);
writeFileSync('photo-resized.jpg', resizedImage);ImageTranscoder (格式轉換)
ImageTranscoder(格式转换)
Convert images between formats (JPEG, PNG, WebP, AVIF, etc.):
typescript
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
// Convert to WebP format
const transcoder = new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4,
});
const jpegBuffer = readFileSync('photo.jpg');
const webpBuffer = await transcoder.convert<Buffer>(jpegBuffer);
writeFileSync('photo.webp', webpBuffer);在不同格式之间转换图片(JPEG、PNG、WebP、AVIF等):
typescript
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
// 转换为WebP格式
const transcoder = new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4,
});
const jpegBuffer = readFileSync('photo.jpg');
const webpBuffer = await transcoder.convert<Buffer>(jpegBuffer);
writeFileSync('photo.webp', webpBuffer);ImageWatermark (浮水印)
ImageWatermark(添加水印)
Add watermarks to images:
typescript
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { gravity } from 'sharp';
// Add watermark at bottom-right corner
const watermarker = new ImageWatermark({
watermarks: [
{
image: readFileSync('logo.png'), // Can be Buffer or file path
gravity: gravity.southeast, // Position: bottom-right
},
],
});
const originalImage = readFileSync('photo.jpg');
const watermarkedImage = await watermarker.convert<Buffer>(originalImage);
writeFileSync('photo-watermarked.jpg', watermarkedImage);为图片添加水印:
typescript
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { gravity } from 'sharp';
// 在右下角添加水印
const watermarker = new ImageWatermark({
watermarks: [
{
image: readFileSync('logo.png'), // 可以是Buffer或文件路径
gravity: gravity.southeast, // 位置:右下角
},
],
});
const originalImage = readFileSync('photo.jpg');
const watermarkedImage = await watermarker.convert<Buffer>(originalImage);
writeFileSync('photo-watermarked.jpg', watermarkedImage);ConverterManager (管道處理)
ConverterManager(管道处理)
Chain multiple converters in a pipeline:
typescript
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { gravity } from 'sharp';
// Create processing pipeline: Resize → Watermark → Convert to WebP
const pipeline = new ConverterManager([
new ImageResizer({
maxWidth: 1200,
maxHeight: 800,
keepAspectRatio: true,
}),
new ImageWatermark({
watermarks: [
{
image: './logo.png',
gravity: gravity.southeast,
},
],
}),
new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4,
}),
]);
const originalImage = readFileSync('photo.jpg');
const processedImage = await pipeline.convert<Buffer>(originalImage);
writeFileSync('processed.webp', processedImage);将多个转换器串联成处理管道:
typescript
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { gravity } from 'sharp';
// 创建处理管道:缩放 → 添加水印 → 转换为WebP
const pipeline = new ConverterManager([
new ImageResizer({
maxWidth: 1200,
maxHeight: 800,
keepAspectRatio: true,
}),
new ImageWatermark({
watermarks: [
{
image: './logo.png',
gravity: gravity.southeast,
},
],
}),
new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4,
}),
]);
const originalImage = readFileSync('photo.jpg');
const processedImage = await pipeline.convert<Buffer>(originalImage);
writeFileSync('processed.webp', processedImage);Common Patterns
常见模式
Buffer vs Stream Processing
Buffer与Stream处理
Buffer - Use for small files (小檔案), faster, uses more memory:
typescript
const buffer = readFileSync('image.jpg');
const result = await converter.convert<Buffer>(buffer);
writeFileSync('output.jpg', result);Stream (Readable) - Use for large files (大檔案), memory efficient:
typescript
import { createReadStream, createWriteStream } from 'fs';
import { Readable } from 'stream';
const stream = createReadStream('large-image.jpg');
const resultStream = await converter.convert<Readable>(stream);
// Pipe to output
resultStream.pipe(createWriteStream('output.jpg'));Buffer - 适用于小文件,速度更快,但占用更多内存:
typescript
const buffer = readFileSync('image.jpg');
const result = await converter.convert<Buffer>(buffer);
writeFileSync('output.jpg', result);Stream(Readable) - 适用于大文件,内存占用更高效:
typescript
import { createReadStream, createWriteStream } from 'fs';
import { Readable } from 'stream';
const stream = createReadStream('large-image.jpg');
const resultStream = await converter.convert<Readable>(stream);
// 输出到文件
resultStream.pipe(createWriteStream('output.jpg'));Single Conversion
单一转换
Process one image with one converter:
typescript
// Resize only
const resizer = new ImageResizer({ maxWidth: 800 });
const resized = await resizer.convert<Buffer>(imageBuffer);
// Convert format only
const transcoder = new ImageTranscoder({ targetFormat: 'webp', quality: 90 });
const webp = await transcoder.convert<Buffer>(jpegBuffer);使用单个转换器处理一张图片:
typescript
// 仅缩放
const resizer = new ImageResizer({ maxWidth: 800 });
const resized = await resizer.convert<Buffer>(imageBuffer);
// 仅转换格式
const transcoder = new ImageTranscoder({ targetFormat: 'webp', quality: 90 });
const webp = await transcoder.convert<Buffer>(jpegBuffer);Pipeline Conversion
管道转换
Chain multiple operations:
typescript
// Three-step processing
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 1000, maxHeight: 750 }),
new ImageWatermark({ watermarks: [{ image: 'logo.png' }] }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
const result = await pipeline.convert<Buffer>(originalImage);串联多个操作:
typescript
// 三步处理
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 1000, maxHeight: 750 }),
new ImageWatermark({ watermarks: [{ image: 'logo.png' }] }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
const result = await pipeline.convert<Buffer>(originalImage);Batch Processing
批量处理
Process multiple images:
typescript
import { glob } from 'glob';
import { basename } from 'path';
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 800 }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
// Find all JPEG files
const images = await glob('photos/*.jpg');
// Process all images
await Promise.all(
images.map(async (imagePath) => {
const buffer = readFileSync(imagePath);
const result = await pipeline.convert<Buffer>(buffer);
const outputName = basename(imagePath, '.jpg') + '.webp';
writeFileSync(`output/${outputName}`, result);
})
);处理多张图片:
typescript
import { glob } from 'glob';
import { basename } from 'path';
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 800 }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
// 查找所有JPEG文件
const images = await glob('photos/*.jpg');
// 处理所有图片
await Promise.all(
images.map(async (imagePath) => {
const buffer = readFileSync(imagePath);
const result = await pipeline.convert<Buffer>(buffer);
const outputName = basename(imagePath, '.jpg') + '.webp';
writeFileSync(`output/${outputName}`, result);
})
);Feature Comparison
功能对比
| Feature | ImageResizer | ImageTranscoder | ImageWatermark | ConverterManager |
|---|---|---|---|---|
| Resize Images (縮放圖片) | Yes | No | No | Via pipeline |
| Convert Formats (轉換格式) | No | Yes | No | Via pipeline |
| Add Watermarks (添加浮水印) | No | No | Yes | Via pipeline |
| Maintain Aspect Ratio (保持縱橫比) | Yes | N/A | N/A | N/A |
| Crop to Fit (裁剪適配) | Yes | No | No | N/A |
| Buffer Support (緩衝支援) | Yes | Yes | Yes | Yes |
| Stream Support (串流支援) | Yes | Yes | Yes | Yes |
| Concurrency Control (並發控制) | Yes | Yes | Yes | N/A |
| Multiple Watermarks (多浮水印) | No | No | Yes | N/A |
| Pipeline Chaining (管道串聯) | No | No | No | Yes |
| 功能 | ImageResizer | ImageTranscoder | ImageWatermark | ConverterManager |
|---|---|---|---|---|
| 图像缩放 | 是 | 否 | 否 | 通过管道实现 |
| 格式转换 | 否 | 是 | 否 | 通过管道实现 |
| 添加水印 | 否 | 否 | 是 | 通过管道实现 |
| 保持宽高比 | 是 | 不适用 | 不适用 | 不适用 |
| 裁剪适配 | 是 | 否 | 否 | 不适用 |
| Buffer支持 | 是 | 是 | 是 | 是 |
| Stream支持 | 是 | 是 | 是 | 是 |
| 并发控制 | 是 | 是 | 是 | 不适用 |
| 多水印支持 | 否 | 否 | 是 | 不适用 |
| 管道串联 | 否 | 否 | 否 | 是 |
Supported Formats (ImageTranscoder)
支持的格式(ImageTranscoder)
| Input Formats | Output Formats |
|---|---|
| jpg, png, webp, gif, avif, tif, svg | jpg/jpeg, png, webp, avif, heif, gif, tif/tiff |
Note: Input formats are detected bylibrary. JPEG uses 'jpg' internally.file-type
內部常數(未導出):
- - 支援的輸入格式陣列:
SupportSources['jpg', 'png', 'webp', 'gif', 'avif', 'tif', 'svg'] - - 當輸入格式不支援時拋出的錯誤類別
UnsupportedSource
| 输入格式 | 输出格式 |
|---|---|
| jpg, png, webp, gif, avif, tif, svg | jpg/jpeg, png, webp, avif, heif, gif, tif/tiff |
注意: 输入格式由库自动检测。JPEG在内部使用'jpg'标识。file-type
内部常量(未导出):
- - 支持的输入格式数组:
SupportSources['jpg', 'png', 'webp', 'gif', 'avif', 'tif', 'svg'] - - 当输入格式不支持时抛出的错误类
UnsupportedSource
ImageTranscoderOptions Types
ImageTranscoderOptions类型
每種輸出格式都有對應的 Sharp options:
typescript
import type { AvifOptions, GifOptions, HeifOptions, JpegOptions, PngOptions, TiffOptions, WebpOptions } from 'sharp';
type ImageTranscoderOptions =
| { targetFormat: 'avif' } & AvifOptions
| { targetFormat: 'heif' } & HeifOptions
| { targetFormat: 'gif' } & GifOptions
| { targetFormat: 'tif' | 'tiff' } & TiffOptions
| { targetFormat: 'png' } & PngOptions
| { targetFormat: 'webp' } & WebpOptions
| { targetFormat: 'jpg' | 'jpeg' } & JpegOptions;
// Constructor 額外支援 concurrency 選項
new ImageTranscoder(options: ImageTranscoderOptions & { concurrency?: number });每种输出格式都有对应的Sharp配置项:
typescript
import type { AvifOptions, GifOptions, HeifOptions, JpegOptions, PngOptions, TiffOptions, WebpOptions } from 'sharp';
type ImageTranscoderOptions =
| { targetFormat: 'avif' } & AvifOptions
| { targetFormat: 'heif' } & HeifOptions
| { targetFormat: 'gif' } & GifOptions
| { targetFormat: 'tif' | 'tiff' } & TiffOptions
| { targetFormat: 'png' } & PngOptions
| { targetFormat: 'webp' } & WebpOptions
| { targetFormat: 'jpg' | 'jpeg' } & JpegOptions;
// 构造函数额外支持concurrency选项
new ImageTranscoder(options: ImageTranscoderOptions & { concurrency?: number });ImageResizerOptions
ImageResizerOptions
typescript
interface ImageResizerOptions {
maxWidth?: number; // 最大寬度(至少需設定 maxWidth 或 maxHeight 其一)
maxHeight?: number; // 最大高度
keepAspectRatio?: boolean; // 保持縱橫比(預設 true,使用 'inside' fit)
concurrency?: number; // 並發數(預設 1)
}Note: 使用確保不會放大小於目標尺寸的圖片。當withoutEnlargement: true時使用keepAspectRatio=false裁剪適配。fit: 'cover'
typescript
interface ImageResizerOptions {
maxWidth?: number; // 最大宽度(至少需设置maxWidth或maxHeight其中一个)
maxHeight?: number; // 最大高度
keepAspectRatio?: boolean; // 保持宽高比(默认true,使用'inside'适配方式)
concurrency?: number; // 并发数(默认1)
}注意: 使用确保不会放大小于目标尺寸的图片。当withoutEnlargement: true时,使用keepAspectRatio=false进行裁剪适配。fit: 'cover'
ImageWatermarkOptions
ImageWatermarkOptions
typescript
interface Watermark {
image: string | Buffer; // 浮水印圖片(檔案路徑或 Buffer)
gravity?: Gravity; // 位置(預設 southeast)
}
interface ImageWatermarkOptions {
watermarks: Watermark[]; // 浮水印陣列(支援多個浮水印)
concurrency?: number; // 並發數(預設 1)
}typescript
interface Watermark {
image: string | Buffer; // 水印图片(文件路径或Buffer)
gravity?: Gravity; // 位置(默认southeast)
}
interface ImageWatermarkOptions {
watermarks: Watermark[]; // 水印数组(支持多个水印)
concurrency?: number; // 并发数(默认1)
}Watermark Positions (ImageWatermark)
水印位置(ImageWatermark)
| Position | Gravity Constant | Description |
|---|---|---|
| Top-Left (左上) | | Northwest corner |
| Top-Center (上中) | | Top center |
| Top-Right (右上) | | Northeast corner |
| Middle-Left (中左) | | Middle left |
| Center (中央) | | Center |
| Middle-Right (中右) | | Middle right |
| Bottom-Left (左下) | | Southwest corner |
| Bottom-Center (下中) | | Bottom center |
| Bottom-Right (右下) | | Southeast corner (default) |
| 位置 | Gravity常量 | 描述 |
|---|---|---|
| 左上 | | 左上角 |
| 上中 | | 顶部中央 |
| 右上 | | 右上角 |
| 中左 | | 左侧中间 |
| 中央 | | 正中央 |
| 中右 | | 右侧中间 |
| 左下 | | 左下角 |
| 下中 | | 底部中央 |
| 右下 | | 右下角(默认) |
Detailed Documentation
详细文档
For complete API reference and advanced usage:
- ImageResizer Complete Reference - All options, methods, and use cases
- ImageTranscoder Complete Reference - Format-specific options, error handling
- ImageWatermark Complete Reference - Watermark configuration, positioning
如需完整的API参考和高级用法:
- ImageResizer完整参考 - 所有配置项、方法和使用场景
- ImageTranscoder完整参考 - 格式专属配置项、错误处理
- ImageWatermark完整参考 - 水印配置、位置设置
Complete Examples
完整示例
E-commerce Product Images
电商产品图片处理
Process product photos: resize for web, add branding watermark, convert to modern format:
typescript
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { readFileSync, writeFileSync } from 'fs';
import { gravity } from 'sharp';
// Create product image processor
const productImageProcessor = new ConverterManager([
// Step 1: Resize to 1200x1200 max
new ImageResizer({
maxWidth: 1200,
maxHeight: 1200,
keepAspectRatio: true,
concurrency: 4,
}),
// Step 2: Add brand watermark at bottom-right
new ImageWatermark({
watermarks: [
{
image: readFileSync('brand-logo.png'),
gravity: gravity.southeast,
},
],
}),
// Step 3: Convert to WebP for smaller file size
new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4, // Higher effort = better compression
}),
]);
// Process a product image
const originalPhoto = readFileSync('product-001-raw.jpg');
const processedPhoto = await productImageProcessor.convert<Buffer>(originalPhoto);
writeFileSync('product-001.webp', processedPhoto);处理产品图片:调整网页适配尺寸、添加品牌水印、转换为现代格式:
typescript
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageWatermark } from '@rytass/file-converter-adapter-image-watermark';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { readFileSync, writeFileSync } from 'fs';
import { gravity } from 'sharp';
// 创建产品图片处理器
const productImageProcessor = new ConverterManager([
// 步骤1:调整至最大1200x1200
new ImageResizer({
maxWidth: 1200,
maxHeight: 1200,
keepAspectRatio: true,
concurrency: 4,
}),
// 步骤2:在右下角添加品牌水印
new ImageWatermark({
watermarks: [
{
image: readFileSync('brand-logo.png'),
gravity: gravity.southeast,
},
],
}),
// 步骤3:转换为WebP格式以减小文件体积
new ImageTranscoder({
targetFormat: 'webp',
quality: 85,
effort: 4, // 更高的effort值对应更好的压缩效果
}),
]);
// 处理产品图片
const originalPhoto = readFileSync('product-001-raw.jpg');
const processedPhoto = await productImageProcessor.convert<Buffer>(originalPhoto);
writeFileSync('product-001.webp', processedPhoto);Large File Stream Processing
大文件流处理
Handle large files efficiently with streams to avoid memory issues:
typescript
import { createReadStream, createWriteStream } from 'fs';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { Readable } from 'stream';
// Create resizer
const resizer = new ImageResizer({
maxWidth: 1920,
maxHeight: 1080,
keepAspectRatio: true,
});
// Process large file using streams
const inputStream = createReadStream('large-photo.jpg');
const outputStream = await resizer.convert<Readable>(inputStream);
// Pipe to output file
outputStream.pipe(createWriteStream('large-photo-resized.jpg'));
// Wait for completion
await new Promise((resolve, reject) => {
outputStream.on('finish', resolve);
outputStream.on('error', reject);
});使用流高效处理大文件,避免内存问题:
typescript
import { createReadStream, createWriteStream } from 'fs';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { Readable } from 'stream';
// 创建缩放器
const resizer = new ImageResizer({
maxWidth: 1920,
maxHeight: 1080,
keepAspectRatio: true,
});
// 使用流处理大文件
const inputStream = createReadStream('large-photo.jpg');
const outputStream = await resizer.convert<Readable>(inputStream);
// 输出到文件
outputStream.pipe(createWriteStream('large-photo-resized.jpg'));
// 等待处理完成
await new Promise((resolve, reject) => {
outputStream.on('finish', resolve);
outputStream.on('error', reject);
});Batch Processing with Progress Tracking
带进度追踪的批量处理
Process multiple images with progress updates:
typescript
import { glob } from 'glob';
import { basename } from 'path';
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
// Create pipeline
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 800, maxHeight: 600, keepAspectRatio: true }),
new ImageTranscoder({ targetFormat: 'webp', quality: 80 }),
]);
// Find all images
const images = await glob('input-photos/*.{jpg,png,jpeg}');
const total = images.length;
let processed = 0;
console.log(`Found ${total} images to process`);
// Process with progress tracking
for (const imagePath of images) {
const buffer = readFileSync(imagePath);
const result = await pipeline.convert<Buffer>(buffer);
const outputName = basename(imagePath).replace(/\.(jpg|png|jpeg)$/, '.webp');
writeFileSync(`output/${outputName}`, result);
processed++;
console.log(`Progress: ${processed}/${total} (${Math.round((processed / total) * 100)}%)`);
}
console.log('All images processed successfully!');处理多张图片并显示进度:
typescript
import { glob } from 'glob';
import { basename } from 'path';
import { ConverterManager } from '@rytass/file-converter';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
// 创建处理管道
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 800, maxHeight: 600, keepAspectRatio: true }),
new ImageTranscoder({ targetFormat: 'webp', quality: 80 }),
]);
// 查找所有图片
const images = await glob('input-photos/*.{jpg,png,jpeg}');
const total = images.length;
let processed = 0;
console.log(`找到${total}张待处理图片`);
// 带进度追踪处理
for (const imagePath of images) {
const buffer = readFileSync(imagePath);
const result = await pipeline.convert<Buffer>(buffer);
const outputName = basename(imagePath).replace(/\.(jpg|png|jpeg)$/, '.webp');
writeFileSync(`output/${outputName}`, result);
processed++;
console.log(`进度:${processed}/${total}(${Math.round((processed / total) * 100)}%)`);
}
console.log('所有图片处理完成!');Conditional Processing
条件处理
Apply different processing based on image properties:
typescript
import sharp from 'sharp';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { ConverterManager } from '@rytass/file-converter';
async function processImage(inputBuffer: Buffer): Promise<Buffer> {
// Get image metadata
const metadata = await sharp(inputBuffer).metadata();
if (!metadata.width || !metadata.height) {
throw new Error('Unable to read image dimensions');
}
// For large images: resize first
if (metadata.width > 2000 || metadata.height > 2000) {
console.log('Large image detected, resizing...');
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 1600, maxHeight: 1600, keepAspectRatio: true }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
return pipeline.convert<Buffer>(inputBuffer);
}
// For small images: just convert format
console.log('Small image detected, converting format only...');
const transcoder = new ImageTranscoder({ targetFormat: 'webp', quality: 90 });
return transcoder.convert<Buffer>(inputBuffer);
}
// Usage
const imageBuffer = readFileSync('photo.jpg');
const result = await processImage(imageBuffer);
writeFileSync('output.webp', result);根据图片属性应用不同的处理逻辑:
typescript
import sharp from 'sharp';
import { ImageResizer } from '@rytass/file-converter-adapter-image-resizer';
import { ImageTranscoder } from '@rytass/file-converter-adapter-image-transcoder';
import { ConverterManager } from '@rytass/file-converter';
async function processImage(inputBuffer: Buffer): Promise<Buffer> {
// 获取图片元数据
const metadata = await sharp(inputBuffer).metadata();
if (!metadata.width || !metadata.height) {
throw new Error('无法读取图片尺寸');
}
// 对于大图片:先缩放
if (metadata.width > 2000 || metadata.height > 2000) {
console.log('检测到大尺寸图片,正在缩放...');
const pipeline = new ConverterManager([
new ImageResizer({ maxWidth: 1600, maxHeight: 1600, keepAspectRatio: true }),
new ImageTranscoder({ targetFormat: 'webp', quality: 85 }),
]);
return pipeline.convert<Buffer>(inputBuffer);
}
// 对于小图片:仅转换格式
console.log('检测到小尺寸图片,仅转换格式...');
const transcoder = new ImageTranscoder({ targetFormat: 'webp', quality: 90 });
return transcoder.convert<Buffer>(inputBuffer);
}
// 使用示例
const imageBuffer = readFileSync('photo.jpg');
const result = await processImage(imageBuffer);
writeFileSync('output.webp', result);Troubleshooting
故障排除
Common Issues
常见问题
Memory Errors with Large Images
- Solution: Use Stream processing instead of Buffer
typescript
// Instead of Buffer
const buffer = readFileSync('huge-image.jpg');
const result = await converter.convert<Buffer>(buffer);
// Use Stream
const stream = createReadStream('huge-image.jpg');
const resultStream = await converter.convert<Readable>(stream);Sharp Cache Issues
- Sharp automatically disables cache in all adapters with
sharp.cache(false) - No action needed
Concurrency Performance
- Adjust option based on your CPU cores
concurrency - Default is 1 (single-threaded)
- Try 4-8 for multi-core systems
typescript
const resizer = new ImageResizer({
maxWidth: 800,
concurrency: 8, // Adjust based on CPU cores
});Unsupported Format Errors (ImageTranscoder)
- Check input format is supported: JPEG, PNG, WebP, GIF, AVIF, TIFF, SVG
- Error will throw exception (internal class, catch using
UnsupportedSourceand checkinstanceof Error)message === 'UnsupportedSource'
Output Quality Issues
- Increase parameter (0-100)
quality - For WebP/AVIF, increase (0-9) for better compression
effort
typescript
const transcoder = new ImageTranscoder({
targetFormat: 'webp',
quality: 95, // Higher quality
effort: 6, // More compression effort
});大图片处理时出现内存错误
- 解决方案:使用Stream处理替代Buffer处理
typescript
// 避免使用Buffer
const buffer = readFileSync('huge-image.jpg');
const result = await converter.convert<Buffer>(buffer);
// 使用Stream
const stream = createReadStream('huge-image.jpg');
const resultStream = await converter.convert<Readable>(stream);Sharp缓存问题
- 所有适配器中已通过自动禁用Sharp缓存
sharp.cache(false) - 无需额外操作
并发性能优化
- 根据CPU核心数调整选项
concurrency - 默认值为1(单线程)
- 多核系统建议设置为4-8
typescript
const resizer = new ImageResizer({
maxWidth: 800,
concurrency: 8, // 根据CPU核心数调整
});格式不支持错误(ImageTranscoder)
- 检查输入格式是否在支持列表中:JPEG、PNG、WebP、GIF、AVIF、TIFF、SVG
- 错误会抛出异常(内部类,可通过
UnsupportedSource并检查instanceof Error捕获)message === 'UnsupportedSource'
输出质量问题
- 提高参数值(范围0-100)
quality - 对于WebP/AVIF格式,提高值(范围0-9)以获得更好的压缩效果
effort
typescript
const transcoder = new ImageTranscoder({
targetFormat: 'webp',
quality: 95, // 更高质量
effort: 6, // 更高压缩耗时,更好效果
});