Създаването на албуми TIFF с много страници директно от облачно съхранение е чудесен начин да архивирате или обменяте големи комплекти изображения (сканиране, снимки на продукти, страници). С Aspose.Imaging за .NET можете да преобразувате stream снимки от Azure Blob Storage (или S3), да ги конвертирате в рамки TifF и да съхранявате едно, компресирано мулти-страници TICF - няма необходими файлове за време.

Тази статия заменя гъстата с комплектен, изчерпателен, копие-паст пример и добавя точни подробности за TIFF опции, ** компресиране** , DPI** и използване на памет.

Комплетен пример (Inline, Copy-Paste Ready)

Какво прави тази програма:

  • Списък на изображенията в контейнер Azure Blob Storage (филтриране по разширение).
  • Поток на всеки блок в паметта (без температурни файлове).
  • Създава multi-page TIFF с помощта на компресия LZW при 300 DPI.
  • Съхранява TIFF на локален диск ** и** (опционално) го изтегля обратно в контейнера.

Изисквания :

  • .NET 8 (или 6+)
  • Пакетът на НУГЕТ:- 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;
    }
}

Защо тези опции?

  • • Компресиране *: TiffLzwRgb дава без загуба компресия и висока съвместимост (идеално за архивиране или размяна).

  • Алтернативите са: TiffDeflateRgb (често по-малки, се нуждаят от дефлатна поддръжка); скениране на bilevel → TiffCcittFax4.

  • • ДПС *: ResolutionSetting(300, 300) е печатно приятен за сканиране; изберете 150 за уеб само за намаляване на размера.

  • „Мемория“ е: RasterImage.CacheData() Подобрява производителността, тъй като източните пиксели са скрити преди копиране на рамката.

  • Задаване на поръчки: Сортирането на имената на блоба осигурява стабилен ред на страницата (например, page_001…page_999).

Изтегляне на албума обратно в облака

Шаблонът ** преминава към локален диск** и незабавно ** възстановява** с помощта на един и същ контейнер. Ако работен поток трябва да избягва локалните файлове изцяло, препратете TIFF към MemoryStream и повика UploadAsync За много големи албуми, предпочитате да спестите на временен файл, за да запазите използването на паметта предсказуемо.

Amazon S3 вариация (Snippet)

Ако сте на S3, логиката е една и съща – заместване на Azure SDK повиквания с 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)
}

Дръжте частите Aspose.Imaging идентични; само кода за списък / изтегляне** се променя.

Управление на грешки и устойчивост

  • ** Празен контейнер/префикс**: приложението излиза мило с съобщение.
  • ** Корумпирана картина**: вградена Image.Load В А try/catch; преодоляване на лошите рамки и продължаване, или аборт въз основа на политика.
  • Много големи комплекти: помислете за хункиране (например, изграждане на един TIFF на 1000 изображения) за ограничаване на размера на файла и границите на сканера/инструмента.
  • Името на файла: включва дата/време или префикс в името на изхода за проследяване (например, album-2025-07-03_1500.tiff).

Най-добрите практики

  • Конзистентни измерения: смесените ориентации/размери са фини, но за единни резултати предварително нормализират изображенията (скала/крутиране) преди копие на рамката.
  • ** Цветна дълбочина**: Скенирането на текста може да се компресира по-добре, ако се конвертира в сива скала преди TIFF монтаж (използвайте Aspose.Imaging филтри).
  • Metadata: Можете да добавите EXIF/IPTC/XMP по рамка, преди да спестите, ако е необходимо.
  • Тестиране: проверяване на резултатите в няколко зрители (Windows Photos, IrfanView, Preview, ImageMagick) и с потребители с нисък поток (DMS, PACS и т.н.).

заключение

Сега имате тестиран модел за изграждане на многострани TIFF албуми директно от Azure Blob Storage (и лесно преносим до S3). Примерът поддържа използването на паметта предсказуемо, използва безпроблемна LZW компресия и поставя практичен 300 DPI дефолт – готов за архивиране, обмен и печат.

Клониране на горепосочения код в вашия проект, струй в връзката / контейнер, и ще имате продуцирани TIFF албуми за минути.

More in this category