Skip to content

示例

本页面提供了 Sharp 的各种使用示例,从基本操作到高级技巧。

基本示例

图像调整大小

javascript
import sharp from 'sharp';

// 调整到固定尺寸
await sharp('input.jpg')
  .resize(300, 200)
  .toFile('resized.jpg');

// 保持宽高比
await sharp('input.jpg')
  .resize(300, null)
  .toFile('resized.jpg');

// 使用不同的适配模式
await sharp('input.jpg')
  .resize(300, 200, {
    fit: 'cover',        // 裁剪以适应
    position: 'center'   // 居中裁剪
  })
  .toFile('cover.jpg');

格式转换

javascript
// JPEG 转 PNG
await sharp('input.jpg')
  .png()
  .toFile('output.png');

// PNG 转 WebP
await sharp('input.png')
  .webp({ quality: 80 })
  .toFile('output.webp');

// 转换为 AVIF
await sharp('input.jpg')
  .avif({ quality: 80 })
  .toFile('output.avif');

创建缩略图

javascript
// 创建正方形缩略图
await sharp('input.jpg')
  .resize(150, 150, { fit: 'cover' })
  .jpeg({ quality: 90 })
  .toFile('thumbnail.jpg');

// 创建不同尺寸的缩略图
const sizes = [150, 300, 600];
const promises = sizes.map(size => 
  sharp('input.jpg')
    .resize(size, size, { fit: 'cover' })
    .jpeg({ quality: 85 })
    .toFile(`thumbnail-${size}.jpg`)
);

await Promise.all(promises);

高级示例

图像合成

javascript
// 在图像上添加水印
await sharp('input.jpg')
  .composite([{
    input: 'watermark.png',
    top: 10,
    left: 10
  }])
  .jpeg()
  .toFile('with-watermark.jpg');

// 创建图像网格
const grid = await sharp({
  create: {
    width: 600,
    height: 400,
    channels: 4,
    background: { r: 255, g: 255, b: 255, alpha: 1 }
  }
})
.composite([
  { input: 'image1.jpg', top: 0, left: 0 },
  { input: 'image2.jpg', top: 0, left: 300 },
  { input: 'image3.jpg', top: 200, left: 0 },
  { input: 'image4.jpg', top: 200, left: 300 }
])
.jpeg()
.toFile('grid.jpg');

滤镜效果

javascript
// 应用多种滤镜
await sharp('input.jpg')
  .blur(3)           // 模糊
  .sharpen()         // 锐化
  .modulate({        // 色彩调整
    brightness: 1.1,
    saturation: 0.8
  })
  .jpeg({ quality: 85 })
  .toFile('filtered.jpg');

// 创建复古效果
await sharp('input.jpg')
  .modulate({
    brightness: 0.9,
    saturation: 0.7,
    hue: 30
  })
  .tint({ r: 255, g: 200, b: 150 })
  .jpeg({ quality: 80 })
  .toFile('vintage.jpg');

批量处理

javascript
import fs from 'fs';
import path from 'path';

async function processDirectory(inputDir, outputDir) {
  const files = fs.readdirSync(inputDir);
  const imageFiles = files.filter(file => 
    /\.(jpg|jpeg|png|webp)$/i.test(file)
  );

  const promises = imageFiles.map(async file => {
    const inputPath = path.join(inputDir, file);
    const outputPath = path.join(outputDir, `processed-${file}`);

    await sharp(inputPath)
      .resize(800, 600, { fit: 'inside' })
      .jpeg({ quality: 80 })
      .toFile(outputPath);

    console.log(`处理完成: ${file}`);
  });

  await Promise.all(promises);
  console.log('所有文件处理完成!');
}

processDirectory('./input', './output');

响应式图像

javascript
// 生成响应式图像
const sizes = [
  { width: 320, suffix: 'sm' },
  { width: 640, suffix: 'md' },
  { width: 1024, suffix: 'lg' },
  { width: 1920, suffix: 'xl' }
];

const formats = ['jpeg', 'webp', 'avif'];

async function generateResponsiveImages(inputFile) {
  const promises = [];

  for (const size of sizes) {
    for (const format of formats) {
      const outputFile = `output-${size.suffix}.${format}`;
      
      let pipeline = sharp(inputFile)
        .resize(size.width, null, { fit: 'inside' });

      switch (format) {
        case 'jpeg':
          pipeline = pipeline.jpeg({ quality: 80 });
          break;
        case 'webp':
          pipeline = pipeline.webp({ quality: 80 });
          break;
        case 'avif':
          pipeline = pipeline.avif({ quality: 80 });
          break;
      }

      promises.push(pipeline.toFile(outputFile));
    }
  }

  await Promise.all(promises);
  console.log('响应式图像生成完成!');
}

generateResponsiveImages('input.jpg');

性能优化示例

流式处理

javascript
import fs from 'fs';

// 处理大文件
const pipeline = sharp()
  .resize(800, 600)
  .jpeg({ quality: 80 });

fs.createReadStream('large-input.jpg')
  .pipe(pipeline)
  .pipe(fs.createWriteStream('output.jpg'));

// 处理多个文件
const processFile = (inputFile, outputFile) => {
  return new Promise((resolve, reject) => {
    sharp(inputFile)
      .resize(300, 200)
      .jpeg({ quality: 80 })
      .pipe(fs.createWriteStream(outputFile))
      .on('finish', resolve)
      .on('error', reject);
  });
};

const files = ['file1.jpg', 'file2.jpg', 'file3.jpg'];
const promises = files.map((file, index) => 
  processFile(file, `output-${index}.jpg`)
);

await Promise.all(promises);

内存优化

javascript
// 使用 Buffer 处理小文件
const buffer = await sharp('input.jpg')
  .resize(300, 200)
  .jpeg({ quality: 80 })
  .toBuffer();

// 使用流处理大文件
const stream = sharp('large-input.jpg')
  .resize(800, 600)
  .jpeg({ quality: 80 });

// 分块处理
const chunkSize = 1024 * 1024; // 1MB
const chunks = [];

stream.on('data', chunk => {
  chunks.push(chunk);
});

stream.on('end', () => {
  const buffer = Buffer.concat(chunks);
  fs.writeFileSync('output.jpg', buffer);
});

并发控制

javascript
// 限制并发数量
async function processWithConcurrency(files, concurrency = 3) {
  const results = [];
  
  for (let i = 0; i < files.length; i += concurrency) {
    const batch = files.slice(i, i + concurrency);
    const batchPromises = batch.map(file => 
      sharp(file)
        .resize(300, 200)
        .jpeg({ quality: 80 })
        .toFile(`processed-${file}`)
    );
    
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
  }
  
  return results;
}

const files = ['file1.jpg', 'file2.jpg', 'file3.jpg', 'file4.jpg'];
await processWithConcurrency(files, 2);

实用工具示例

图像信息提取

javascript
async function getImageInfo(file) {
  const metadata = await sharp(file).metadata();
  const stats = await sharp(file).stats();
  
  return {
    filename: file,
    format: metadata.format,
    width: metadata.width,
    height: metadata.height,
    size: metadata.size,
    channels: metadata.channels,
    isOpaque: stats.isOpaque,
    dominantColor: stats.dominant
  };
}

const info = await getImageInfo('input.jpg');
console.log('图像信息:', info);

图像比较

javascript
async function compareImages(file1, file2) {
  const [stats1, stats2] = await Promise.all([
    sharp(file1).stats(),
    sharp(file2).stats()
  ]);
  
  return {
    file1: stats1,
    file2: stats2,
    dominantDiff: {
      r: Math.abs(stats1.dominant.r - stats2.dominant.r),
      g: Math.abs(stats1.dominant.g - stats2.dominant.g),
      b: Math.abs(stats1.dominant.b - stats2.dominant.b)
    }
  };
}

const comparison = await compareImages('image1.jpg', 'image2.jpg');
console.log('图像比较结果:', comparison);

图像验证

javascript
async function validateImage(file) {
  try {
    const metadata = await sharp(file).metadata();
    
    const validation = {
      isValid: true,
      format: metadata.format,
      width: metadata.width,
      height: metadata.height,
      size: metadata.size,
      errors: []
    };
    
    // 检查尺寸
    if (metadata.width > 5000 || metadata.height > 5000) {
      validation.errors.push('图像尺寸过大');
    }
    
    // 检查文件大小
    if (metadata.size > 10 * 1024 * 1024) { // 10MB
      validation.errors.push('文件大小过大');
    }
    
    // 检查格式
    const allowedFormats = ['jpeg', 'png', 'webp'];
    if (!allowedFormats.includes(metadata.format)) {
      validation.errors.push('不支持的格式');
    }
    
    if (validation.errors.length > 0) {
      validation.isValid = false;
    }
    
    return validation;
  } catch (error) {
    return {
      isValid: false,
      errors: [error.message]
    };
  }
}

const validation = await validateImage('input.jpg');
console.log('验证结果:', validation);

完整应用示例

javascript
import sharp from 'sharp';
import fs from 'fs';
import path from 'path';

class ImageProcessor {
  constructor(options = {}) {
    this.options = {
      quality: 80,
      maxWidth: 1920,
      maxHeight: 1080,
      ...options
    };
  }

  async processImage(inputPath, outputPath, options = {}) {
    try {
      const metadata = await sharp(inputPath).metadata();
      
      // 计算调整尺寸
      const { width, height } = this.calculateDimensions(
        metadata.width,
        metadata.height,
        options
      );
      
      // 处理图像
      await sharp(inputPath)
        .resize(width, height, { fit: 'inside' })
        .jpeg({ quality: this.options.quality })
        .toFile(outputPath);
      
      return {
        success: true,
        originalSize: { width: metadata.width, height: metadata.height },
        newSize: { width, height },
        outputPath
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  calculateDimensions(originalWidth, originalHeight, options = {}) {
    const { maxWidth = this.options.maxWidth, maxHeight = this.options.maxHeight } = options;
    
    let width = originalWidth;
    let height = originalHeight;
    
    if (width > maxWidth) {
      height = (height * maxWidth) / width;
      width = maxWidth;
    }
    
    if (height > maxHeight) {
      width = (width * maxHeight) / height;
      height = maxHeight;
    }
    
    return { width: Math.round(width), height: Math.round(height) };
  }

  async batchProcess(inputDir, outputDir) {
    if (!fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir, { recursive: true });
    }

    const files = fs.readdirSync(inputDir);
    const imageFiles = files.filter(file => 
      /\.(jpg|jpeg|png|webp)$/i.test(file)
    );

    const results = [];
    
    for (const file of imageFiles) {
      const inputPath = path.join(inputDir, file);
      const outputPath = path.join(outputDir, `processed-${file}`);
      
      const result = await this.processImage(inputPath, outputPath);
      results.push({ file, ...result });
    }
    
    return results;
  }
}

// 使用示例
const processor = new ImageProcessor({ quality: 85 });

// 处理单个文件
const result = await processor.processImage('input.jpg', 'output.jpg');

// 批量处理
const results = await processor.batchProcess('./input', './output');

console.log('处理结果:', results);

下一步

Released under the Apache 2.0 License.