Stvaranje multi-page TIFF albuma izravno iz skladištenja u oblaku je odličan način za arhiviranje ili razmjenu velikih skupova slika (scans, fotografije proizvoda, stranice slike). sa Aspose.Imaging za .NET, možete stream slike iz Azure Blob Storage (ili S3), pretvoriti ih u TifF okvirima, i sačuvati jedinstvenu, komprimiranu multi-pagina TFF – nema temp datoteke potrebne.

Ovaj članak zamjenjuje gnoj sa kompletnim, upotrijebljenim, primjerom kopiranja i dodaje točne detalje za TIFF opcije, ** kompresija**.

Priprema za kopiranje (Inline, Copy-Paste Ready)

Što ovaj program radi:

  • Popis slike u Azure Blob Storage posudu (filtriranje po proširenju).
  • Svaki blob protječe u memoriju (bez temp datoteke).
  • Izgrađuje multi-page TIFF pomoću LZW kompresije na 300 DPI.
  • Uštedi TIFF na lokalni disk ** i** (opcionalno) vraća ga nazad na posudu.

Vrijednosti :

  • .NET 8 (ili 6+)
  • Slijedeći paketi:- 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;
    }
}

Zašto ove opcije?

  • • Kompresija *: TiffLzwRgb daje bez gubitka kompresije i visoku kompatibilnost (idealan za arhiviranje ili razmjenu).

  • Alternativne opcije : TiffDeflateRgb (Često manji, zahtijeva Deflate podršku); bilevel skeniranje → TiffCcittFax4.

    • Slijedeći članak *: ResolutionSetting(300, 300) Pritisak je prikladan za skeniranje; odaberite 150 za web-only kako biste smanjili veličinu.
  • • sjećanje *: RasterImage.CacheData() poboljšava performanse jer su izvorni pikseli sakriveni prije kopiranja okvira.

  • Naručivanje: razvrstavanje imena blob osigurava stabilan redoslijed stranice (npr. page_001…page_999).

Slijedeći članakAlbum se vraća u oblak

Uzorak slijedi na lokalni disk i odmah uplaćuje nazad koristeći isti kontejner. ako vaš radni protok treba izbjeći lokalne datoteke u potpunosti, prenesite TIFF na MemoryStream i poziva UploadAsync Za vrlo velike albume, bolje je uštedjeti na temporary datoteku kako bi se upotreba memorije mogla predvidjeti.

Amazon S3 varijanta (snippet)

Ako ste na S3, logika je ista – zamijenite Azure SDK pozive s AWS SDk poziva:

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

Držite Aspose.Imaging dijelove identične; samo se listing/downloading kod mijenja.

Pogrešna rješavanja i otpornost

  • Prazni kontejner/prefix: aplikacija izlazi milosrdno s porukom.
  • Korupcijska slika*: Wrap Image.Load u A try/catchIzbjegavajte loše okvire i nastavite, ili abortus na temelju politike.
  • Vrlo veliki setovi: uzmite u obzir šunkiranje (na primjer, napravite jedan TIFF po 1000 slika) kako biste ograničili veličinu datoteke i granice skenera / alata.
  • ** Naziv datoteke**: uključuje datum/koji ili predviđanje u ime izlaska za praćenje (npr. album-2025-07-03_1500.tiff).

Najbolje prakse

  • Konsistentne dimenzije: miješane orijentacije/izaze su dobre, ali za jedinstvene rezultate pre-normalizirati slike (skala/rotat) prije kopiranja okvir.
  • Dubina boje: skeniranje teksta može se bolje komprimirati ako se pretvori u grayscale prije sastavljanja TIFF-a (koristite filtre Aspose.Imaging).
  • Metadata: možete priključiti EXIF/IPTC/XMP po okvirima prije spašavanja ako je potrebno.
  • Testing: provjerite izlazak u više gledatelja (Windows Fotografije, IrfanView, Preview, ImageMagick) i s potrošačima koji dolaze (DMS, PACS, itd.).

zaključak

Sada imate testirani uzorak za izgradnju multi-page TIFF albuma izravno iz Azure Blob Storage (i lako se prenosi na S3). Primjer čuva upotrebu memorije predvidljivom, koristi bez gubitka LZW kompresije i postavlja praktičan 300 DPI default – spreman za arhiviranje, razmjenu i tiskanje.

Klonirajte gornji kod u svoj projekt, žice u vašoj poveznici / kontejneru, a dobit ćete TIFF albume razine proizvodnje za nekoliko minuta.

More in this category