การสร้าง albums TIFF หลายหน้าโดยตรงจากจัดเก็บคลาวด์เป็นวิธีที่ดีในการเก็บรวบรวมหรือแลกเปลี่ยนชุดภาพขนาดใหญ่ (การสแกนภาพผลิตภัณฑ์ภาพหน้า) ด้วย Aspose.Imaging สําหรับ .NET คุณสามารถ stream ภาพจาก Azure Blob Storage (หรือ S3), แปลงพวกเขาเป็นกรอบ TifF, และบันทึกไฟล์ tiff multi-page คอมเพรสเดี่ยว - ไม่จําเป็น.
บทความนี้จะแทนที่ตัวอักษรด้วยตัวอย่าง สมบูรณ์, inline, copy-paste และเพิ่มรายละเอียดที่แม่นยําสําหรับ ตัวเลือก TIFF, การบีบอัด DPI และการใช้หน่วยความจํา **
ตัวอย่างเต็มรูปแบบ (Inline, Copy-Paste Ready)
สิ่งที่โปรแกรมนี้ทํา:
- รายการภาพในคอนเทนเนอร์ ** Azure Blob Storage** (กรองตามขยาย)
- กระแสแต่ละบล็อกไปยังหน่วยความจํา (ไม่มีไฟล์ temp)
- สร้าง 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) การสแกน bilevelTiffCcittFax4
.** DPI * :
ResolutionSetting(300, 300)
เป็นมิตรกับการพิมพ์สําหรับการสแกน เลือก 150 สําหรับเว็บเท่านั้นเพื่อลดขนาด*หน่วยความจํา *
RasterImage.CacheData()
ปรับปรุงประสิทธิภาพเนื่องจากพิกเซลแหล่งกําเนิดถูกเข้ารหัสก่อนการคัดลอกเฟรมคําสั่งซื้อ: การจัดอันดับชื่อ blob ช่วยให้การสั่งซื้อหน้าที่มั่นคง (เช่น
page_001…page_999
).
ดาวน์โหลด albums back to cloud
ตัวอย่าง ส่งไปยังไดรฟ์ท้องถิ่น และทันที อัปโหลดกลับ โดยใช้ภาชนะเดียวกัน หากการไหลงานของคุณควรหลีกเลี่ยงไฟล์ท้องฟ้าอย่างสมบูรณ์โปรดส่ง TIFF ไปยัง MemoryStream
และโทร UploadAsync
สําหรับอัลบั้มขนาดใหญ่มากโปรดบันทึกไว้ในไฟล์ชั่วคราว** เพื่อให้การใช้งานของหน่วยความจําสามารถคาดการณ์ได้
รุ่น Amazon S3 (Snippet)
หากคุณอยู่ใน S3 โลกที่เหมือนกัน—แทนที่การโทร SDK ของ Azure กับการเรียกร้อง 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 เป็นที่เหมือนกันเท่านั้นที่เปลี่ยนรหัส รายการ/ดาวน์โหลด
การจัดการข้อผิดพลาด & ความต้านทาน
- คอนเทนเนอร์ที่ว่างเปล่า / Prefix: แอพลิเคชันจะออกด้วยความสุขด้วยข้อความ
- รูปภาพที่เสียหาย: wrap
Image.Load
ใน Atry/catch
; ลบกรอบที่ไม่ดีและต่อเนื่องหรือการระงับโดยพื้นฐานของนโยบาย - ชุดขนาดใหญ่มาก: โปรดพิจารณาการสแกน (เช่นสร้าง TIFF ต่อ 1,000 ภาพ) เพื่อ จํากัด ขนาดไฟล์และขีด จํากัด ของ scanner/tool
- ชื่อไฟล์: รวมวันที่ / เวลาหรือค่าเริ่มต้นในชื่อการส่งออกสําหรับการติดตาม (เช่น
album-2025-07-03_1500.tiff
).
การปฏิบัติที่ดีที่สุด
- มิติที่สอดคล้อง: คู่มือผสม / ชนิดมีความดี แต่สําหรับผลลัพธ์ที่สมบูรณ์แบบภาพ (ขนาด / รูต) ก่อนการคัดลอกกรอบ
- ความลึกสี: การสแกนข้อความสามารถบีบอัดได้ดีขึ้นหากมีการแปลงเป็นสีเทาก่อนการประกอบ TIFF (ใช้กรอง Aspose.Imaging)
- Metadata: คุณสามารถเชื่อมต่อ EXIF/IPTC/XMP ต่อกรอบก่อนที่จะบันทึกหากจําเป็น
- การทดสอบ: ตรวจสอบผลผลิตในผู้ชมหลาย (Windows Photos, IrfanView, Preview, ImageMagick) และกับผู้บริโภคต่ํา (DMS, PACS, ฯลฯ)
ข้อสรุป
ตอนนี้คุณมีรูปแบบที่ได้รับการทดสอบ** เพื่อสร้าง albums TIFF แบบหลายหน้า** โดยตรงจาก Azure Blob Storage** (และพกพาได้อย่างง่ายดายไปยัง S3) ตัวอย่างนี้ช่วยให้การใช้งานของหน่วยความจําสามารถคาดการณ์ได้ใช้การบีบอัด LZW ที่ไม่มีการสูญเสีย** และตั้งค่าตัวเลือกแบบกําหนดเอง 300 DPI พร้อมสําหรับการจัดเก็บแลกเปลี่ยนและพิมพ์
Clone the code above into your project, ไฟในสายการเชื่อมต่อ / container ของคุณ, และคุณจะได้รับ TIFF อัลบั้มระดับการผลิตในนาที.
More in this category
- การเพิ่มประสิทธิภาพของ GIF ของ animated ใน .NET โดยใช้ Aspose.Imaging
- Optimize Multi-Page TIFFs for Archival in .NET ด้วย Aspose
- HEIC ไปยัง JPEG / PNG แปลงด้วย Aspose.Imaging สําหรับ .NET
- การเคลื่อนไหวที่ขับเคลื่อนข้อมูลใน .NET ด้วย Aspose.Imaging
- การบีบอัดภาพที่มีคุณภาพและไม่มีการสูญเสียใน .NET ด้วย Aspose.Imaging