Crear álbumes TIFF de múltiples páginas directamente desde el almacenamiento en la nube es una gran manera de archivar o intercambiar grandes conjuntos de imágenes (escans, fotos de producto, imágenes de página). con Aspose.Imaging para .NET, puede stream imágenes del Azure Blob Storage (o S3), convertirlas en cuadros TifF, y ahorrar un único, comprimido multi-páginas T IFF - no se requieren archivos de tiempo.

Este artículo reemplaza el huevo con un ejemplo de completo, en línea, copia-paste y añade detalles precisos para opciones TIFF, compresión , DPI e utilización de memoria.

Exemplos completos (Inline, Copy-Paste Ready)

Lo que hace este programa:

  • Listar las imágenes en un contenedor Azure Blob Storage (filtrado por extensión).
  • Streams cada blob en la memoria (no archivos temp).
  • Construye un multi-page TIFF utilizando la composición LZW a 300 DPI.
  • Salva el TIFF al disco local ** y** (opcional) lo carga de nuevo al contenedor.

Requisitos:

  • .NET 8 (o 6+)
  • NuGet paquetes:- Aspose.Imaging
  • Azure.Storage.Blobs
// File: Program.cs
// Build deps:
//   dotnet add package Aspose.Imaging
//   dotnet add package Azure.Storage.Blobs
//
// Run (example):
//   setx AZURE_STORAGE_CONNECTION_STRING "<your-connection-string>"
//   dotnet run -- "<container-name>" "album-output.tiff"  // uploads album-output.tiff back to same container
//
// Notes:
// - Streams JPEG/PNG/BMP/TIFF/GIF web-safe inputs and assembles a multi-page LZW RGB TIFF at 300 DPI.
// - If there are no images, the program exits gracefully.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Azure;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Aspose.Imaging;
using Aspose.Imaging.FileFormats.Tiff;
using Aspose.Imaging.FileFormats.Tiff.Enums;
using Aspose.Imaging.ImageOptions;

class Program
{
    // Accepted extensions (case-insensitive)
    private static readonly string[] ImageExts = new[]
    {
        ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tif", ".tiff"
    };

    static async Task<int> Main(string[] args)
    {
        if (args.Length < 2)
        {
            Console.Error.WriteLine("Usage: dotnet run -- <containerName> <albumFileName.tiff> [prefix]");
            Console.Error.WriteLine("Example: dotnet run -- scans album-2025-07.tiff scans/incoming/");
            return 1;
        }

        string containerName = args[0];
        string albumFileName = args[1];
        string prefix = args.Length > 2 ? args[2] : string.Empty;

        string? conn = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");
        if (string.IsNullOrWhiteSpace(conn))
        {
            Console.Error.WriteLine("AZURE_STORAGE_CONNECTION_STRING is not set.");
            return 2;
        }

        try
        {
            var container = new BlobContainerClient(conn, containerName);

            // 1) Enumerate candidate image blobs (optionally under a prefix)
            var images = await ListImageBlobsAsync(container, prefix);
            if (images.Count == 0)
            {
                Console.WriteLine("No images found. Nothing to do.");
                return 0;
            }

            Console.WriteLine($"Found {images.Count} image(s). Building multi-page TIFF…");

            // 2) Build multipage TIFF in memory (for safety, stream to file to avoid huge RAM for very large sets)
            //    We will construct a TiffImage and append frames.
            string localAlbumPath = Path.GetFullPath(albumFileName);
            BuildMultipageTiffFromBlobs(container, images, localAlbumPath);

            Console.WriteLine($"✅ Saved multi-page TIFF locally: {localAlbumPath}");

            // 3) Optional: upload back to same container
            var albumBlob = container.GetBlobClient(Path.GetFileName(albumFileName));
            Console.WriteLine($"Uploading album back to container as: {albumBlob.Name} …");
            using (var fs = File.OpenRead(localAlbumPath))
            {
                await albumBlob.UploadAsync(fs, overwrite: true);
            }
            Console.WriteLine("✅ Upload complete.");
            return 0;
        }
        catch (RequestFailedException are)
        {
            Console.Error.WriteLine("Azure error: " + are.Message);
            return 3;
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine("Error: " + ex.Message);
            return 4;
        }
    }

    private static async Task<List<BlobItem>> ListImageBlobsAsync(BlobContainerClient container, string prefix)
    {
        var result = new List<BlobItem>();
        await foreach (var item in container.GetBlobsAsync(prefix: prefix))
        {
            // Skip virtual folders
            if (item.Properties.BlobType != BlobType.Block)
                continue;

            if (HasImageExtension(item.Name))
                result.Add(item);
        }

        // Optional: stable order by name (e.g., page_001.jpg … page_999.jpg)
        result.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
        return result;
    }

    private static bool HasImageExtension(string blobName)
    {
        string ext = Path.GetExtension(blobName) ?? string.Empty;
        return ImageExts.Contains(ext, StringComparer.OrdinalIgnoreCase);
    }

    private static void BuildMultipageTiffFromBlobs(BlobContainerClient container, List<BlobItem> images, string outputTiffPath)
    {
        // TIFF encoder defaults:
        // - LZW compression is a good balance of size & compatibility for RGB images.
        // - 300 DPI is print-friendly; change if you need web-only output.
        var tiffOptions = new TiffOptions(TiffExpectedFormat.TiffLzwRgb)
        {
            ResolutionSettings = new ResolutionSetting(300, 300)
        };

        TiffImage? tiff = null;
        try
        {
            for (int index = 0; index < images.Count; index++)
            {
                var blobClient = container.GetBlobClient(images[index].Name);
                Console.WriteLine($"Downloading & adding: {blobClient.Name}");

                using var ms = new MemoryStream();
                blobClient.DownloadTo(ms);
                ms.Position = 0;

                // Load the image with Aspose.Imaging (auto-detects format)
                using var src = Image.Load(ms);
                // Cache pixel data to speed up frame copy (especially for network streams)
                if (src is RasterImage raster)
                    raster.CacheData();

                // Create a TIFF frame by copying from the source image
                // NOTE: TiffFrame.CopyFrame(tiffOptions, <RasterImage>) is preferred when available
                TiffFrame frame;
                if (src is RasterImage rimg)
                {
                    frame = TiffFrame.CopyFrame(tiffOptions, rimg);
                }
                else
                {
                    // Fallback: render non-raster formats into a rasterized frame
                    frame = CreateRasterFrameFromAny(src, tiffOptions);
                }

                if (index == 0)
                {
                    // First frame defines the TiffImage
                    tiff = new TiffImage(frame);
                }
                else
                {
                    tiff!.AddFrame(frame);
                }
            }

            if (tiff == null)
                throw new InvalidOperationException("No frames were created. Aborting.");

            // Save to local TIFF file
            tiff.Save(outputTiffPath);
        }
        finally
        {
            tiff?.Dispose();
        }
    }

    private static TiffFrame CreateRasterFrameFromAny(Image src, TiffOptions opts)
    {
        // Create a blank frame and draw the source into it
        // This is a compatibility path if the loaded image isn’t a RasterImage
        var frame = new TiffFrame(opts, src.Width, src.Height);
        using (var graphics = new Aspose.Imaging.Graphics(frame))
        {
            graphics.Clear(Aspose.Imaging.Color.White);
            graphics.DrawImage(src, new Aspose.Imaging.Rectangle(0, 0, src.Width, src.Height));
        }
        return frame;
    }
}

¿Por qué estas opciones?

  • • Compresión *: TiffLzwRgb proporciona compresión sin pérdida y alta compatibilidad (ideal para archivos o intercambios).

  • Las alternativas: TiffDeflateRgb (a menudo más pequeño, necesita soporte Deflate); escaneos de bilevel TiffCcittFax4.

    • El DPI: ResolutionSetting(300, 300) Es impreso-friendly para las escaneas; seleccione 150 para la web solo para reducir el tamaño.
  • • Memoria *: RasterImage.CacheData() Mejora el rendimiento porque los píxeles de fuente se cachan antes de copiar el marco.

  • Order: La clasificación de los nombres de blob garantiza una orden de página estable (por ejemplo, page_001…page_999).).

Descargar el álbum de nuevo en la nube

La muestra save al disco local y inmediatamente upload back usando el mismo contenedor. Si su flujo de trabajo debe evitar los archivos locales en su totalidad, fluye el TIFF a un MemoryStream y llamar UploadAsync Para los álbumes muy grandes, prefiere ahorrar en un archivo temporario para mantener el uso de la memoria previsible.

Amazon S3 variante (snippet)

Si está en S3, la lógica es la misma: reemplazar las llamadas de SDK de Azure con las de AWS:

// NuGet:
//   dotnet add package AWSSDK.S3

using Amazon.S3;
using Amazon.S3.Model;

// Listing:
using var s3 = new AmazonS3Client(Amazon.RegionEndpoint.APSouth1);
var list = await s3.ListObjectsV2Async(new ListObjectsV2Request
{
    BucketName = "your-bucket",
    Prefix = "images/"
});
foreach (var obj in list.S3Objects.Where(o => HasImageExtension(o.Key)))
{
    using var get = await s3.GetObjectAsync("your-bucket", obj.Key);
    using var ms = new MemoryStream();
    await get.ResponseStream.CopyToAsync(ms);
    ms.Position = 0;

    using var src = Image.Load(ms);
    // (same TiffFrame.CopyFrame logic as above)
}

Mantenga las partes Aspose.Imaging idénticas; solo los cambios en el código lista/descarga.

Tratamiento de errores y resiliencia

  • Container vacío/prefix: la aplicación sale graciosamente con un mensaje.
  • ** Imagen corrompida**: enrojecimiento Image.Load En el A try/catchEscape los malos cuadros y continúa, o aborto basado en la política.
  • Muy grandes conjuntos: considere chunking (por ejemplo, crear un TIFF por 1.000 imágenes) para limitar los tamaños de archivos y los límites del escáner/tool.
  • Nombre de archivo: incluye fecha/hora o prefixo en el nombre de salida para la rastreabilidad (por ejemplo, album-2025-07-03_1500.tiff).).

Las mejores prácticas

  • Dimensiones consistentes: las orientaciones mixtas/as son buenas, pero para los resultados uniformes pre-normalizan las imágenes (escala/rotado) antes de copiar el marco.
  • La profundidad de color: las escaneas de texto pueden ser mejor comprimidas si se convierten en griego antes de la asamblea TIFF (uso de filtros Aspose.Imaging).
  • Metadatos: puede añadir EXIF/IPTC/XMP por marco antes de guardar si es necesario.
  • Testing: verificación de la salida en múltiples espectadores (Windows Photos, IrfanView, Preview, ImageMagick) y con consumidores descendentes (DMS, PACS, etc.).

Conclusión

Ahora tiene un patrón testado para construir albumes TIFF de múltiples páginas directamente desde Azure Blob Storage (y fácilmente portable a S3). El ejemplo mantiene el uso de la memoria predictable, utiliza compresión LZW sin pérdida, y establece un estándar práctico 300 DPI – listo para el archivo, el intercambio y la impresión.

Clone el código anteriormente en su proyecto, corriente en la línea de conexión / contenedor, y usted tendrá álbumes TIFF de grado de producción en minutos.

More in this category