Skip to content

Ejemplos

Esta página proporciona varios ejemplos de uso de Sharp, desde operaciones básicas hasta técnicas avanzadas.

Ejemplos Básicos

Redimensionamiento de Imágenes

javascript
import sharp from 'sharp';

// Redimensionar a tamaño fijo
await sharp('input.jpg')
  .resize(300, 200)
  .toFile('resized.jpg');

// Mantener relación de aspecto
await sharp('input.jpg')
  .resize(300, null)
  .toFile('resized.jpg');

// Usar diferentes modos de ajuste
await sharp('input.jpg')
  .resize(300, 200, {
    fit: 'cover',        // Recortar para ajustar
    position: 'center'   // Recortar centrado
  })
  .toFile('cover.jpg');

Conversión de Formatos

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

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

// Convertir a AVIF
await sharp('input.jpg')
  .avif({ quality: 80 })
  .toFile('output.avif');

Crear Miniatura

javascript
// Crear miniatura cuadrada
await sharp('input.jpg')
  .resize(150, 150, { fit: 'cover' })
  .jpeg({ quality: 90 })
  .toFile('thumbnail.jpg');

// Crear miniaturas de diferentes tamaños
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);

Ejemplos Avanzados

Composición de Imágenes

javascript
// Agregar marca de agua en imagen
await sharp('input.jpg')
  .composite([{
    input: 'watermark.png',
    top: 10,
    left: 10
  }])
  .jpeg()
  .toFile('with-watermark.jpg');

// Crear cuadrícula de imágenes
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');

Efectos de Filtro

javascript
// Aplicar múltiples filtros
await sharp('input.jpg')
  .blur(3)           // Desenfoque
  .sharpen()         // Nitidez
  .modulate({        // Ajuste de color
    brightness: 1.1,
    saturation: 0.8
  })
  .jpeg({ quality: 85 })
  .toFile('filtered.jpg');

// Crear efecto vintage
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');

Procesamiento por Lotes

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(`Procesamiento completado: ${file}`);
  });

  await Promise.all(promises);
  console.log('¡Todos los archivos procesados!');
}

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

Imágenes Adaptativas

javascript
// Generar imágenes adaptativas
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('¡Generación de imágenes adaptativas completada!');
}

generateResponsiveImages('input.jpg');

Ejemplos de Optimización de Rendimiento

Procesamiento por Streams

javascript
import fs from 'fs';

// Procesar archivos grandes
const pipeline = sharp()
  .resize(800, 600)
  .jpeg({ quality: 80 });

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

// Procesar múltiples archivos
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);

Optimización de Memoria

javascript
// Usar Buffer para procesar archivos pequeños
const buffer = await sharp('input.jpg')
  .resize(300, 200)
  .jpeg({ quality: 80 })
  .toBuffer();

// Usar streams para procesar archivos grandes
const stream = sharp('large-input.jpg')
  .resize(800, 600)
  .jpeg({ quality: 80 });

// Procesamiento por bloques
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);
});

Control de Concurrencia

javascript
// Limitar número de concurrencia
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);

Ejemplos de Herramientas de Utilidad

Extracción de Información de Imagen

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('Información de imagen:', info);

Comparación de Imágenes

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('Resultado de comparación de imágenes:', comparison);

Validación de Imágenes

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: []
    };
    
    // Verificar dimensiones
    if (metadata.width > 5000 || metadata.height > 5000) {
      validation.errors.push('Dimensiones de imagen demasiado grandes');
    }
    
    // Verificar tamaño de archivo
    if (metadata.size > 10 * 1024 * 1024) { // 10MB
      validation.errors.push('Tamaño de archivo demasiado grande');
    }
    
    // Verificar formato
    const allowedFormats = ['jpeg', 'png', 'webp'];
    if (!allowedFormats.includes(metadata.format)) {
      validation.errors.push('Formato no soportado');
    }
    
    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('Resultado de validación:', validation);

Ejemplo de Aplicación Completa

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();
      
      // Calcular dimensiones de ajuste
      const { width, height } = this.calculateDimensions(
        metadata.width,
        metadata.height,
        options
      );
      
      // Procesar imagen
      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;
  }
}

// Ejemplo de uso
const processor = new ImageProcessor({ quality: 85 });

// Procesar archivo individual
const result = await processor.processImage('input.jpg', 'output.jpg');

// Procesamiento por lotes
const results = await processor.batchProcess('./input', './output');

console.log('Resultados del procesamiento:', results);

Próximos Pasos

Liberado bajo la Licencia Apache 2.0.