Skip to content

Ví dụ

Trang này cung cấp các ví dụ sử dụng khác nhau của Sharp, từ thao tác cơ bản đến kỹ thuật nâng cao.

Ví dụ cơ bản

Thay đổi kích thước hình ảnh

javascript
import sharp from 'sharp';

// Thay đổi đến kích thước cố định
await sharp('input.jpg')
  .resize(300, 200)
  .toFile('resized.jpg');

// Giữ tỷ lệ khung hình
await sharp('input.jpg')
  .resize(300, null)
  .toFile('resized.jpg');

// Sử dụng các chế độ phù hợp khác nhau
await sharp('input.jpg')
  .resize(300, 200, {
    fit: 'cover',        // Cắt để phù hợp
    position: 'center'   // Cắt ở giữa
  })
  .toFile('cover.jpg');

Chuyển đổi định dạng

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

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

// Chuyển đổi sang AVIF
await sharp('input.jpg')
  .avif({ quality: 80 })
  .toFile('output.avif');

Tạo hình thu nhỏ

javascript
// Tạo hình thu nhỏ vuông
await sharp('input.jpg')
  .resize(150, 150, { fit: 'cover' })
  .jpeg({ quality: 90 })
  .toFile('thumbnail.jpg');

// Tạo hình thu nhỏ với các kích thước khác nhau
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);

Ví dụ nâng cao

Tổng hợp hình ảnh

javascript
// Thêm watermark lên hình ảnh
await sharp('input.jpg')
  .composite([{
    input: 'watermark.png',
    top: 10,
    left: 10
  }])
  .jpeg()
  .toFile('with-watermark.jpg');

// Tạo lưới hình ảnh
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');

Hiệu ứng bộ lọc

javascript
// Áp dụng nhiều bộ lọc
await sharp('input.jpg')
  .blur(3)           // Làm mờ
  .sharpen()         // Làm sắc nét
  .modulate({        // Điều chỉnh màu sắc
    brightness: 1.1,
    saturation: 0.8
  })
  .jpeg({ quality: 85 })
  .toFile('filtered.jpg');

// Tạo hiệu ứng cổ điển
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');

Xử lý hàng loạt

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(`Xử lý hoàn tất: ${file}`);
  });

  await Promise.all(promises);
  console.log('Tất cả tệp đã được xử lý!');
}

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

Hình ảnh đáp ứng

javascript
// Tạo hình ảnh đáp ứng
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('Tạo hình ảnh đáp ứng hoàn tất!');
}

generateResponsiveImages('input.jpg');

Ví dụ tối ưu hiệu suất

Xử lý luồng

javascript
import fs from 'fs';

// Xử lý tệp lớn
const pipeline = sharp()
  .resize(800, 600)
  .jpeg({ quality: 80 });

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

// Xử lý nhiều tệp
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);

Tối ưu bộ nhớ

javascript
// Sử dụng Buffer để xử lý tệp nhỏ
const buffer = await sharp('input.jpg')
  .resize(300, 200)
  .jpeg({ quality: 80 })
  .toBuffer();

// Sử dụng luồng để xử lý tệp lớn
const stream = sharp('large-input.jpg')
  .resize(800, 600)
  .jpeg({ quality: 80 });

// Xử lý theo khối
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);
});

Điều khiển đồng thời

javascript
// Giới hạn số lượng đồng thời
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);

Ví dụ công cụ tiện ích

Trích xuất thông tin hình ảnh

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('Thông tin hình ảnh:', info);

So sánh hình ảnh

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('Kết quả so sánh hình ảnh:', comparison);

Xác thực hình ảnh

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: []
    };
    
    // Kiểm tra kích thước
    if (metadata.width > 5000 || metadata.height > 5000) {
      validation.errors.push('Kích thước hình ảnh quá lớn');
    }
    
    // Kiểm tra kích thước tệp
    if (metadata.size > 10 * 1024 * 1024) { // 10MB
      validation.errors.push('Kích thước tệp quá lớn');
    }
    
    // Kiểm tra định dạng
    const allowedFormats = ['jpeg', 'png', 'webp'];
    if (!allowedFormats.includes(metadata.format)) {
      validation.errors.push('Định dạng không được hỗ trợ');
    }
    
    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('Kết quả xác thực:', validation);

Ví dụ ứng dụng đầy đủ

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();
      
      // Tính toán kích thước điều chỉnh
      const { width, height } = this.calculateDimensions(
        metadata.width,
        metadata.height,
        options
      );
      
      // Xử lý hình ảnh
      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;
  }
}

// Ví dụ sử dụng
const processor = new ImageProcessor({ quality: 85 });

// Xử lý một tệp
const result = await processor.processImage('input.jpg', 'output.jpg');

// Xử lý hàng loạt
const results = await processor.batchProcess('./input', './output');

console.log('Kết quả xử lý:', results);

Bước tiếp theo

Được phát hành theo Giấy phép Apache 2.0.