Tạo album đa trang TIFF** trực tiếp từ lưu trữ đám mây là một cách tuyệt vời để lưu hoặc trao đổi các bộ ảnh lớn (các quét, hình ảnh sản phẩm, ảnh trang). Với Aspose.Imaging cho .NET, bạn có thể stream ảnh từ Azure Blob Storage (hoặc S3), chuyển đổi chúng thành các khung TifF, và lưu một đơn, phức tạp multi-page TFF – không có tệp thời gian cần thiết.

Bài viết này thay thế lưỡi bằng một ví dụ ** đầy đủ, inline, copy-paste** và thêm chi tiết chính xác cho TIFF tùy chọn, thiết áp , DPI**, và ** sử dụng bộ nhớ**.

Ví dụ đầy đủ (Inline, Copy-Paste Ready)

Chương trình này làm gì:

  • Danh sách hình ảnh trong một container Azure Blob Storage (các bộ lọc theo mở rộng).
  • Stream each blob into memory (không có tệp temp).
  • Xây dựng một TIFF đa trang** bằng cách áp dụng LZW tại 300 DPI.
  • Tiết kiệm TIFF vào ổ đĩa địa phương ** và** (tùy chọn) tải nó trở lại container.

Yêu cầu:

  • .NET 8 (hoặc 6+)
  • Các gói NuGet:- 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;
    }
}

Tại sao lại là những lựa chọn này?

    • Tiết kiệm *: TiffLzwRgb cung cấp lossless nén và tương thích cao (tốt nhất cho lưu trữ hoặc trao đổi).
  • Thay thế : TiffDeflateRgb (thường nhỏ hơn, cần hỗ trợ Deflate); đánh giá bilevel → TiffCcittFax4.

    • Đánh giá *: ResolutionSetting(300, 300) là dễ in cho quét; chọn 150 cho web chỉ để giảm kích thước.
    • Bộ nhớ *: RasterImage.CacheData() cải thiện hiệu suất vì các pixel nguồn được cache trước khi sao chép khung.
  • Đặt hàng: Đặt tên blob đảm bảo thứ tự trang ổn định (ví dụ: page_001…page_999).

Tải album trở lại đám mây

Mẫu save vào ổ đĩa địa phương và ngay lập tức upload trở lại sử dụng cùng một container. nếu dòng công việc của bạn nên tránh các tập tin cục bộ hoàn toàn, lưu TIFF đến một MemoryStream và gọi UploadAsync Đối với các album rất lớn, ưu tiên tiết kiệm cho một tập tin temporary để giữ cho sử dụng bộ nhớ có thể dự đoán được.

Amazon S3 biến thể (snippet)

Nếu bạn đang ở trên S3, logic là tương tự – thay thế các cuộc gọi Azure SDK với các Cuộc gọi AWS SDk:

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

Giữ các phần Aspose.Imaging giống nhau; chỉ có mã listing/downloading thay đổi.

Lỗi xử lý & độ bền

    • Containers / Prefix *: Ứng dụng xuất hiện một cách dễ thương với một thông điệp.
    • Hình ảnh hư hỏng*: Wrap Image.Load trong a try/catch· vượt qua các khung xấu và tiếp tục, hoặc phá thai dựa trên chính sách.
  • Một bộ rất lớn: xem xét chunking (ví dụ, xây dựng một TIFF cho mỗi 1.000 hình ảnh) để giới hạn kích cỡ tệp và biên giới scanner/tool.
  • Tên tệp: bao gồm ngày/giờ hoặc tiền tệ trong tên xuất khẩu cho khả năng theo dõi (ví dụ: album-2025-07-03_1500.tiff).

Thực hành tốt nhất

  • Dimensions liên tục: các định hướng / hình dạng hỗn hợp là tốt, nhưng cho kết quả thống nhất, hình ảnh (độ phân / xoay) được chuẩn hóa trước khi sao chép khung.
  • Một độ sâu màu: các quét văn bản có thể được nén tốt hơn nếu được chuyển đổi thành màu xám trước khi lắp ráp TIFF (các bộ lọc Aspose.Imaging được sử dụng).
  • Metadata: Bạn có thể thêm EXIF/IPTC/XMP mỗi khung trước khi tiết kiệm nếu cần thiết.
  • Testing: xác minh kết quả trong nhiều người xem (Windows Photos, IrfanView, Preview, ImageMagick) và với người tiêu dùng downstream (DMS, PACS, vv).

Kết luận

Bây giờ bạn có một mô hình được thử nghiệm** để xây dựng các album TIFF đa trang** trực tiếp từ Azure Blob Storage** (và dễ dàng di chuyển đến S3). ví dụ này giữ cho việc sử dụng bộ nhớ có thể dự đoán được, dùng không mất LZW nén, và thiết lập một mặc định thực tế 300 DPI – sẵn sàng để lưu trữ, trao đổi và in.

Clone mã trên vào dự án của bạn, dây trong dây kết nối / container, và bạn sẽ có các album TIFF cấp sản xuất trong vài phút.

More in this category