Crearea albumelor TIFF cu mai multe pagini direct de la stocarea cloud este o modalitate excelentă de a arhiva sau schimba seturi de imagini mari (scane, fotografii de produs, imaginile de pagină). cu Aspose.Imaging pentru .NET, puteți stream imaginilor din Azure Blob Storage (sau S3), le converti în ramuri TifF, și salvați un singur, compresat multi-pagini TFF – nu sunt necesare fișierele temp.

Acest articol înlocuiește ghidul cu un exemplu de complete, inline, copie-paste și adaugă detalii exacte pentru opțiunile TIFF, compresie , DPI si utilizare a memoriei.

Exemplu complet (Inline, Copy-Paste Ready)

Ce face acest program:

  • Lista imaginilor într-un container Azure Blob Storage (filtrarea prin extensie).
  • Stream fiecare blob în memorie (nu fișiere temp).
  • Construiește o multi-page TIFF folosind compresia LZW la 300 DPI.
  • Salvează TIFF la discul local ** și** (opțional) îl încărcă înapoi în container.

Cerințele sunt:

  • .NET 8 (sau 6+)
  • Pacientele nu sunt:- 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;
    }
}

De ce aceste opțiuni?

  • • Compresie * : TiffLzwRgb oferă compresie fără pierdere și compatibilitate ridicată (ideală pentru arhivare sau schimb).

  • Alternativă : TiffDeflateRgb (de multe ori mai mic, are nevoie de suport Deflate); scanări bilevel → TiffCcittFax4.

    • În cazul DPI: ResolutionSetting(300, 300) este ușor de imprimat pentru scanare; selectați 150 pentru web numai pentru a reduce dimensiunea.
  • • Memorie * : RasterImage.CacheData() Îmbunătățește performanța, deoarece pixelele de sursă sunt cache înainte de a copia cadru.

  • Orderare: Sortarea numelor blob asigură o ordine stabilă a paginii (de exemplu, page_001…page_999).

Încărcați albumul înapoi în cloud

Eșantionul deschide pe disc local și imediat încărcă înapoi folosind același container. dacă fluxul de lucru ar trebui să evite complet fișierele locale, transmiteți TIFF la un MemoryStream şi apelul UploadAsync Pentru albume foarte mari, preferă să economisească la un fișier temporar** pentru a menține utilizarea memoriei previzibilă.

Amazon S3 variantă (snippet)

Dacă sunteți pe S3, logica este aceeași – înlocuiți apelurile Azure SDK cu apeluri AWSSDK:

// 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)
}

Păstrați părțile Aspose.Imaging identice; numai codul listing/downloading se schimbă.

Gestionarea erorilor și reziliența

  • ** Container gol/prefix**: aplicația iese cu milă cu un mesaj.
  • ** Imaginea coruptă**: Wrap Image.Load În a try/catch· să scape de cadre proaste și să continue, sau avort bazat pe politică.
  • Seturi foarte mari: luați în considerare chunking (de exemplu, construiți un TIFF pe 1.000 de imagini) pentru a limita dimensiunile fișierului și limitele scanner/tool.
  • Numele fișierului: include data / ora sau prefixul în numele de ieșire pentru urmărire (de exemplu, album-2025-07-03_1500.tiff).

Cele mai bune practici

  • ** Dimensiuni coerente**: orientările/dimensiunile amestecate sunt fine, dar pentru rezultatele uniforme pre-normalizarea imaginilor (scala/rotarea) înainte de copierea cadrului.
  • Color de adâncime: scanarea textului poate fi mai bine compresă dacă este convertită în grayscale înainte de asamblarea TIFF (utilizând filtre Aspose.Imaging).
  • Metadata: puteți adăuga EXIF/IPTC/XMP pe cadru înainte de a salva, dacă este necesar.
  • Testare: Verificarea rezultatelor în mai mulți vizualizatori (Windows Photos, IrfanView, Preview, ImageMagick) și cu consumatori de jos (DMS, PACS, etc.).

concluziile

Acum aveți un model testat pentru a construi albume TIFF de mai multe pagini** direct de la Azure Blob Storage (și ușor de transportat către S3). exemplul păstrează utilizarea memoriei previzibilă, utilizează compresia LZW fără pierdere** și stabilește un standard practic 300 DPI – gata pentru arhivare, schimb și imprimare.

Clonați codul de mai sus în proiectul dumneavoastră, lansați-l în conexiunea / container și veți avea albume TIFF de grad de producție în câteva minute.

More in this category