ایجاد آلبوم های چند صفحه ای TIFF** مستقیما از ذخیره سازی ابر یک راه عالی برای آرشیو و یا تبادل مجموعه های تصویر بزرگ (اسکن ها، عکس های محصول، تصاویر صفحه). با Aspose.Imaging برای .NET، شما می توانید stream تصاویر از Azure Blob Storage (یا S3)، آنها را به چارچوب های TifF تبدیل کنید، و صرفه جویی در یک واحد، فشرده چند صفحات T IFF – بدون فایل های زمان مورد نیاز.
این مقاله جایگزین یخ با یک ** کامل، در خط، کپی-پست نمونه** و اضافه کردن جزئیات دقیق برای ** گزینه های TIFF**، ** فشرده سازی**, ** DPI** ، و ** استفاده از حافظه**.
نمونه کامل (Inline، Copy-Paste Ready)
این برنامه چه کار می کند:
- لیست تصاویر در یک مخزن ** Azure Blob Storage** (فیلتر با افزونه)
- هر بلیط را به حافظه (بدون فایلهای تام) جریان دهید.
- ساخت یک ** چند صفحه 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
(اغلب کوچکتر، نیاز به پشتیبانی از Deflate) اسکن های bilevelTiffCcittFax4
.*تصویری از :
ResolutionSetting(300, 300)
چاپ دوستانه برای اسکن است؛ انتخاب 150 برای وب فقط برای کاهش اندازه.- حافظه * :
RasterImage.CacheData()
عملکرد را بهبود می بخشد زیرا پیکسل های منبع قبل از کپی چارچوب پنهان می شوند.
- حافظه * :
** سفارش**: ترتیب نام blob اطمینان حاصل می کند که سفارش صفحه پایدار است (به عنوان مثال،
page_001…page_999
).
دانلود آلبوم به ابر
نمونه ** به دیسک محلی می رود** و بلافاصله ** بارگذاری می شود** با استفاده از همان کانتینر.اگر جریان کار شما باید از فایل های محل به طور کامل اجتناب کند، TIFF را به یک MemoryStream
و تماس UploadAsync
برای آلبوم های بسیار بزرگ، ترجیح می دهید به یک فایل ** موقت** صرفه جویی کنید تا استفاده از حافظه قابل پیش بینی باشد.
آمازون S3 (تغییرمسیر)
اگر شما در 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
در Atry/catch
از بین بردن چارچوب های بد و ادامه، یا سقط جنین بر اساس سیاست. - ** مجموعه های بسیار بزرگ**: در نظر بگیرید که برای محدود کردن اندازه فایل ها و محدودیت های اسکنر / ابزار (به عنوان مثال، ایجاد یک TIFF در هر 1000 تصویر) استفاده کنید.
- ** نام فایل**: شامل تاریخ / زمان یا پیش فرض در نام خروجی برای ردیابی (به عنوان مثال،
album-2025-07-03_1500.tiff
).
بهترین شیوه ها
- مجموعه های متناقض: جهت گیری های مخلوط / اندازه ها خوب هستند، اما برای نتایج یکنواخت، تصاویر (در مقیاس / چرخش) قبل از کپی چارچوب به صورت پیش استاندارد می شوند.
- ** عمق رنگ**: اسکن های متن می تواند بهتر فشرده شود اگر قبل از جمع آوری 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
- بهینه سازی GIF های متحرک در .NET با استفاده از Aspose.Imaging
- بهینه سازی TIFF های چند صفحه ای برای آرشیو در .NET با Aspose
- HEIC به JPEG/PNG تبدیل با Aspose.Imaging برای .NET
- استخراج تصاویر محصول برای پلتفرم های تجارت الکترونیک با استفاده از Aspose.Imaging برای .NET
- انیمیشن های مبتنی بر داده در .NET با Aspose.Imaging