Luomalla multi-page TIFF albumeja suoraan pilvipalvelusta on erinomainen tapa arkistoida tai vaihtaa suuria kuvankerroksia (skannat, tuotteen valokuvat, sivunkuvat). Aspose.Imaging for .NET, voit stream kuvia Azure Blob Storage (tai S3), muuntaa ne TifF-kehyksiin ja tallentaa yhden, tiivistetyn monivuotisen T IFF – ei temp tiedostoja vaaditaan.

Tässä artikkelissa korvataan kuori täydellä, linjassa, kopioi-paste esimerkillä ja lisätään täsmällisiä tietoja TIFF-vaihtoehdoista, kompressiosta sekä DPI-käytöstä.

Täydellinen esimerkki (Inline, Copy-Paste Ready)

Mitä tämä ohjelma tekee:

  • Luettelo kuvia Azure Blob Storage -tallissa (suodatetaan laajennuksen mukaan).
  • Virtaa jokainen blob muistiin (ei temp-tiedostoja).
  • Rakenna multi-page TIFF käyttämällä LZW -kompressiota 300 DPI.
  • Tallentaa TIFF paikalliselle levylle ** ja** (vaihtoehtoisesti) ladata se takaisin säiliöön.

Vaatimukset :

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

Miksi nämä vaihtoehdot?

  • • Kompressiivinen * TiffLzwRgb antaa huonon kompressiota ja korkea yhteensopivuus (ideaalinen arkistointiin tai vaihtoon).

  • Vaihtoehtoja : TiffDeflateRgb ( Usein pienempi, tarvitsee Deflatin tukea); bilevelin skannat → TiffCcittFax4.

    • Säätiö * : ResolutionSetting(300, 300) on tulostusystävällinen skannauksille; valitse 150 vain verkossa kokoon pienentämiseksi.
  • • Muistot * RasterImage.CacheData() parantaa suorituskykyä, koska lähdepikselit on tallennettu ennen kerman kopiointia.

  • Tilaus: Blob-nimien sortointi takaa vakaan sivun järjestyksen (esim. page_001…page_999).

Lataa albumin takaisin pilviin

Näytteet hävitetään paikalliselle levylle ja välittömästi latautuu takaisin käyttämällä samaa säiliötä.Jos työnkulun pitäisi välttää paikallisia tiedostoja kokonaan, siirrä TIFF: n MemoryStream ja soittaa UploadAsync Erittäin suurille albumeille, mieluummin säästää temporary tiedosto pitää muistin käyttöä ennustettavissa.

Amazon S3 vaihtoehto (snippet)

Jos olet S3:ssa, logiikka on sama – korvaa Azure SDK-puhelut AWS SDk- puheluilla:

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

Pidä Aspose.Imaging osat identtinä; vain listing/downloading koodi muuttuu.

Virheiden käsittely ja kestävyys

  • ** Tyhjä säiliö/säätiö**: sovellus lähtee ystävällisesti viestin kanssa.
  • Korruptinen kuva: Wrap Image.Load ja a try/catch; poistaa huonoja kehyksiä ja jatkaa, tai abortti politiikan perusteella.
  • Liian suuret joukot: harkitse työntämistä (esimerkiksi luo yksi TIFF 1000 kuvaa kohden) tiedoston koon ja skannerin/työkalun rajojen rajoittamiseksi.
  • ** Tiedoston nimi**: sisältää päivämäärän / ajan tai ennakkotietokannan jäljitettävyyttä varten (esim. album-2025-07-03_1500.tiff).

Parhaat käytännöt

  • Yhdenmukaiset ulottuvuudet: sekoitetut suuntaukset/muodot ovat hienoja, mutta yhtenäisten tulosten osalta kuvioita (skalaus/kierros) ennakoidaan ennen kaavan kopiointia.
  • ** Väri syvyys**: tekstin skannat voivat kompressoida paremmin, jos ne muunnetaan grayscale ennen TIFF: n kokoonpanoa (käytä Aspose.Imaging -suodattimia).
  • Metadata: Voit liittää EXIF/IPTC/XMP:tä kerralla ennen tallennusta tarvittaessa.
  • Testing: tarkistaa tuloksen useissa katsojissa (Windows Photos, IrfanView, Preview, ImageMagick) ja kuluttajilla (DMS, PACS jne.).

johtopäätöksiä

Nyt sinulla on testattu malli, jolla voit rakentaa multi-page TIFF albumeja suoraan Azure Blob Storage (ja helposti siirrettävissä S3). Esimerkki pitää muistin käyttöä ennustettavissa, käyttää hävittämätöntä LZW-kompressiota ja asettaa käytännön 300 DPI oletusarvo - valmis arkistointiin, vaihtoon ja tulostukseen.

Klonoi edellä oleva koodi projektisi, laita linkki / säiliösi, ja sinulla on tuotantokelpoisia TIFF-albumeja minuutteissa.

More in this category