At oprette multi-page TIFF album direkte fra cloud-opbevaring er en god måde at arkivere eller udveksle store billeder (scans, produktfoto, sidebilleder). Med Aspose.Imaging for .NET, kan du stream billeder fra Azure Blob Storage (eller S3), konvertere dem til TifF-rammer, og gemme en enkelt, komprimeret multi-side TFF – ingen temp filer kræves.

Denne artikel erstattes med en komplet, inline, kopi-paste eksempel og tilføjer nøjagtige detaljer for TIFF-muligheder, compression , DPI samt memory-brug.

Komplet eksemplar (inline, kopiering klar)

Hvad dette program gør:

  • Liste billeder i en Azure Blob Storage container (filtrering ved udvidelse).
  • Strøm hver blob ind i hukommelsen (ikke temp filer).
  • Byg en multi-page TIFF ved hjælp af LZW kompression på 300 DPI.
  • Sparer TIFF til den lokale disk ** og** (alternativt) oplader den tilbage til containeren.

Kravene er:

  • .NET 8 (eller 6+)
  • NuGet pakker:- 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;
    }
}

Hvorfor disse muligheder?

  • • Komprimering *: TiffLzwRgb giver lossless kompression og høj kompatibilitet (ideel til arkivering eller udveksling).

  • Alternativer til: TiffDeflateRgb (ofte mindre, har brug for Deflate-støtte); bilevel scans → TiffCcittFax4.

    • Det er dp* ResolutionSetting(300, 300) er udskrivningsvenlig til scanninger; vælg 150 for web alene for at reducere størrelsen.
  • ”Memorie” er: RasterImage.CacheData() Forbedrer ydeevnen, fordi kilde pixels er cachet før ramme kopi.

  • Ordering: Sortering af blob-navn sikrer en stabil sideordre (f.eks. page_001…page_999).

Download albummet tilbage til skyen

Mønsteret siger til den lokale disk og straks oplader tilbage ved hjælp af samme container. Hvis dit arbejdsproces bør undgå lokale filer fuldt ud, kan TIFF strømme til en MemoryStream og kalde UploadAsync For meget store albums, foretrækker du at spare på en temporær fil for at holde hukommelsessystemet forudsigeligt.

Amazon S3 variant (snippet)

Hvis du er på S3, er logikken den samme – erstatte Azure SDK-opkald med AWS-SDK opkald:

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

Hold Aspose.Imaging dele identiske; kun listen/downloading koden ændres.

Fejlbehandling og modstandsdygtighed

  • Fuck container/prefix: Appen går ud med en besked.
  • Korrupt billede: Wrap Image.Load I A try/catch· Skift dårlige rammer og fortsætte, eller abort baseret på politik.
  • Vær store sæt: Overvej chunking (f.eks. opbygge en TIFF pr. 1.000 billeder) for at begrænse filstørrelser og scanner/værktøjsgrænser.
  • Filenamning: Indtast dato/tid eller præfix i udgangsnavnet for sporbarhed (f.eks. album-2025-07-03_1500.tiff).

Bedste praksis

  • ** Konsistente dimensioner**: Blandede orienteringer/typer er fine, men for ensartede resultater for-normaliserer billeder (skala/rot) før ramme kopiering.
  • Kolor dybde: tekstskanning kan komprimere bedre, hvis konverteret til grayscale før TIFF assembly (brug Aspose.Imaging filtre).
  • Metadata: Du kan tilføje EXIF/IPTC/XMP pr. ramme, før du sparer, hvis det er nødvendigt.
  • Testing: verifikere udgangen i flere seere (Windows Photos, IrfanView, Preview, ImageMagick) og med downstream forbrugerne (DMS, PACS osv.).

Konklusion

Du har nu et testet mønster til at opbygge multi-page TIFF albums direkte fra Azure Blob Storage (og let overførbart til S3). eksemplet holder hukommelsessystemet forudsigeligt, bruger sløs LZW kompression, og sætter en praktisk 300 DPI standard – klar til arkivering, udveksling og print.

Klon koden ovenfor ind i dit projekt, tråd i din forbindelsestring / container, og du vil have produktionsgrad TIFF album i minutter.

More in this category