יצירת אלבומים TIFF רב עמודים ישירות מהאחסון בענן היא דרך מצוינת לארגן או להחליף קבוצות תמונות גדולות (סריקות, תמונות של מוצרים, תמונות של דף).עם Aspose.Imaging עבור .NET, אתה יכול stream תמונות מ- Azure Blob Storage (או S3), להמיר אותם לתוך מסגרות TifF, ולשמור יחיד, דחיסה רב עמודים TFF – אין קבצי טמפרטורה נדרשים.

מאמר זה מחליף את הדבק עם דוגמה מלאה, מקוונת, עותק-פסט ומוסיף פרטים מדויקים עבור אפשרויות TIFF, הדבק , DPI ו שימוש בזיכרון.

דוגמה מלאה (Inline, Copy-Paste Ready)

מה התוכנית עושה:

  • רשימה של תמונות במכולת Azure Blob Storage (סנן לפי הרחבה).
  • זרם כל בלוב לתוך הזיכרון (אין קבצי טמפרטורה).
  • יצירת TIFF רב עמוד ** באמצעות LZW דחיסה ב 300 DPI.
  • שמור את TIFF לדיסק המקומי ** ו** (אופציונלי) מעלה אותו בחזרה לתוך המכולת.

דרישות :

  • .NET 8 (או 6+)
  • חבילות 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;
    }
}

למה את האפשרויות האלה?

  • • דחיסה : TiffLzwRgb נותן ** ללא הפסד* דחיסה ותאימות גבוהה (אידיאלי עבור ארכיון או חילופי).

  • אלטרנטיביות : TiffDeflateRgb (לעיתים קרובות קטנים יותר, דורשים תמיכה Deflate); סריקות bilevel → TiffCcittFax4.

  • תגית: DPI : ResolutionSetting(300, 300) הוא ידידותי להדפסה לסריקות; בחר 150 עבור האינטרנט בלבד כדי להפחית את הגודל.

  • • זיכרון *: RasterImage.CacheData() משפר את הביצועים כי פיקסלים מקוריים מאוחסנים לפני העתקה מסגרת.

  • הזמנה: הגדרת שמות בלוב מבטיחה סדר דף יציב (לדוגמה, page_001…page_999).

להעלות את האלבום בחזרה לענן

הדגימה לוחכת לדיסק המקומי ומיד להעלות בחזרה באמצעות אותו מחסן.אם זרימת העבודה שלך צריכה להימנע קבצים מקומיים לחלוטין, לעבור את TIFF ל- MemoryStream והתקשר UploadAsync עבור אלבומים גדולים מאוד, עדיף לחסוך לקובץ ** זמני** כדי לשמור על שימוש בזיכרון צפוי.

אמזון S3 Variant (Snippet)

אם אתה ב- S3, ההיגיון הוא זהה – להחליף את שיחות ה- Azure SDK לשיחות AWSSDK:

// 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 זהים; רק שינויים בקוד Listing/Downloading.

ניהול שגיאות & עמידות

  • מכולת ריקה/הקובץ המוקדם: האפליקציה יוצאת בחסד עם הודעה.
    • תמונה שבורת *: מעורבב Image.Load ב A try/catch· לפספס את הקבצים הרעים ולהמשיך, או הפלה על בסיס מדיניות.
    • קבוצות גדולות מאוד**: יש לקחת בחשבון (לדוגמה, לבנות TIFF אחד לכל 1,000 תמונות) כדי להגביל את גודל הקובץ ואת הגבולות של סורק / כלי.
  • שם הקובץ: כולל תאריך/שעת או מראש בשם היציאה למטרות מעקב (לדוגמה, album-2025-07-03_1500.tiff).

השיטות הטובות ביותר

  • מדידות עקביות: ההנחיות המערבבות / התכונות טובות, אבל עבור תוצאות חד משמעיות מראש נורמלי תמונות (מגוון / סיבוב) לפני העתק מסגרת.
  • עומק צבע: סריקת טקסט עשויה להתכווץ טוב יותר אם הוחלף ל-Grayscale לפני איסוף TIFF (שימוש בסנני Aspose.Imaging).
  • Metadata: באפשרותך להוסיף EXIF/IPTC/XMP לפי מסגרת לפני שמור, אם יש צורך.
  • Testing: לבדוק את היציאה במגוון צופים (Windows Photos, IrfanView, Preview, ImageMagick) ועם משתמשים נמוכים (DMS, PACS, וכו ‘).

מסקנה

עכשיו יש לך דפוס מבחן** לבניית אלבומים TIFF רב-דפים ישירות מ Azure Blob Storage (ולקל לשאת ל S3).הדוגמה שומרת על השימוש בזיכרון צפוי, משתמשת ב-LZW ללא הפסד**, ומציבה תבנית מעשית של 300 DPI – מוכנה לארכיון, חילופי והדפסה.

הקלד את הקוד לעיל לתוך הפרויקט שלך, קובץ בתוך רצועת הקשר / הקונטינר, ואתה תקבל אלבומים TIFF ברמה של ייצור בדקות.

More in this category