Mit Aspose.Imaging für .NET können Sie Stream Bilder von Azure Blob Storage (oder S3), konvertieren sie in TIFF-Frame und speichern eine einzige, komprimierte Multi-Page Tiff-File – keine Temp-Dateien erforderlich.
Dieser Artikel ersetzt den Gier mit einem vollständigen, inline, Kopier-Past Beispiel und fügt genaue Details hinzu für TIFF Optionen, *Kompression, DPI und Memory Usage.
Vollständiges Beispiel (Inline, Copy-Paste Ready)
Was dieses Programm tut:
- Listen Sie Bilder in einem Azure Blob Storage Container (Filterung durch Erweiterung).
- Streamt jedes Blob in das Gedächtnis (nicht Temp-Dateien).
- Erstellen Sie eine Multi-Page TIFF mit LZW Kompression bei 300 DPI.
- Speichert die TIFF auf den lokalen Disk ** und** (optional) laden sie zurück in den Container.
Anforderungen:
- .NET 8 (oder 6+)
- Pakete von 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;
}
}
Warum diese Optionen?
- Komprimierung *
TiffLzwRgb
bietet lossless Kompression und hohe Kompatibilität (ideal für Archivierung oder Wechsel).
- Komprimierung *
Alternativen zu:
TiffDeflateRgb
(meist kleiner, benötigt Deflate-Support); bilevel scans →TiffCcittFax4
.- In der DPI:
ResolutionSetting(300, 300)
ist Druckfreundlich für Scan; wählen Sie 150 für Web nur, um die Größe zu reduzieren.
- In der DPI:
- Erinnerung *
RasterImage.CacheData()
verbessert die Leistung, da die Quellpixel vor der Frame Copy verschlüsselt werden.
- Erinnerung *
Ordering: Die Sortierung von Blob-Namen gewährleistet eine stabile Page Order (z. B.
page_001…page_999
).).
Laden Sie das Album zurück in die Cloud
Die Probe sage auf den lokalen Disk und sofort upload back mit demselben Container. Wenn Ihr Workflow lokale Dateien vollständig vermeiden sollte, übertragen Sie die TIFF auf eine MemoryStream
und rufen UploadAsync
Für sehr große Albums sparen Sie vorzugsweise auf eine ** temporäre Datei**, um die Speicheranwendung vorhersehbar zu halten.
Amazon S3 Variante (Snippet)
Wenn Sie auf S3 sind, ist die Logik das gleiche – ersetzen Sie Azure SDK-Anrufe mit AWS-SDK Anrufen:
// 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)
}
Halten Sie die Aspose.Imaging Teile identisch; nur der listing/downloading Code ändert sich.
Fehlerbehandlung & Widerstand
- Woll Container/Prefix: Die App entlässt mit einer Nachricht.
- ** Korrupt Bild**: Wrap
Image.Load
In Atry/catch
; schieben schlechte Frame und fortsetzen, oder Abtreibung basierend auf Politik. - Mehr große Sets: Betrachten Sie Chunking (z. B. erstellen Sie einen TIFF pro 1000 Bilder) um die Dateigröße und die Scanner/Tool-Grenzen zu beschränken.
- File-Namen: Datum/Zeit oder Vorschrift im Ausgangsname für die Traceability enthalten (z. B.
album-2025-07-03_1500.tiff
).).
Beste Praktiken
- ** Konsistente Dimensionen**: Gemischte Orientierungen/Siegen sind gut, aber für einheitliche Ergebnisse prä-normalisieren Bilder (Skala/Rota) vor Frame Copy.
- Farbe Tiefe: Text-Scans können besser komprimiert werden, wenn sie vor der TIFF-Sammlung in Grayscale umgewandelt werden (Anwendung von Aspose.Imaging-Filtern).
- Metadata: Sie können EXIF/IPTC/XMP pro Frame hinzufügen, bevor Sie bei Bedarf speichern.
- Testing: Überprüfen Sie die Ausgabe in mehreren Zuschauern (Windows Photos, IrfanView, Preview, ImageMagick) und mit Downstream-Konsumenten (DMS, PACS usw.).
Schlussfolgerungen
Sie haben jetzt ein testes Muster für die Erstellung von Multi-Page TIFF-Alben direkt aus Azure Blob Storage (und leicht zu S3 übertragen). Das Beispiel hält die Speicherverwendung vorhersehbar, verwendet lossless LZW-Kompression und stellt eine praktische 300 DPI-Default fest - bereit für Archivierung, Austausch und Druck.
Klonieren Sie den oben genannten Code in Ihr Projekt, wenden Sie sie in Ihre Verbindungstring/Konteiner, und Sie werden TIFF-Alben in Produktionsstufen in Minuten haben.