C#'ta Toplu PDF İşleme: Belgeleri Ölçekli Otomatikleştirin

This article was translated from English: Does it need improvement?
Translated
View the article in English

IronPDF ile C#'ta Toplu PDF işleme, .NET geliştiricilerinin belge iş akışlarını ölçekli otomatikleştirmesini sağlar — paralel HTML'den PDF'ye dönüştürme ve toplu birleştirme/ayırmadan, yerleşik hata ayıklama, yineleme mantığı ve kontrol noktalı eşzamanlı PDF boru hatlarına kadar. IronPDF'in thread-safe Chromium motoru ve IDisposable tabanlı bellek yönetimi, ister on-premise, ister Azure Functions, AWS Lambda veya Kubernetes'te çalıştırıyor olun, yüksek erişim PDF otomasyonu için özel olarak tasarlanmış.

Kısa Başlangıç Rehberi

Bu eğitim, C#'ta ölçeklenebilir PDF otomasyonunu kapsar — paralel dönüşüm ve toplu işlemlerden, bulut dağıtımı ve dayanıklı boru hatları kalıplarına kadar.

  • Bu kimler içindir: .NET geliştiricileri ve doküman ağırlıklı iş akışlarından sorumlu mimarlar için — belge göç projeleri, günlük rapor üretim boru hatları, uyumluluk düzeltme süpürmeleri veya seri işlemeye uygun olmayan arşiv dijitalleştirme çabaları.
  • Ne inşa edeceksiniz: Parallel.ForEach ile paralel HTML'den PDF'ye dönüşüm, toplu birleştirme ve bölme işlemleri, eş zamanlılık kontrolü için SemaphoreSlim ile async hatlar, başarısızlıkta atlama ve yeniden deneme mantığı ile hata yönetimi, çökme kurtarma için kontrol noktası/devam etme desenleri ve Azure Functions, AWS Lambda ve Kubernetes için bulut dağıtım yapılandırmaları.
  • Nerede çalışır: .NET 6+, .NET Framework 4.6.2+, .NET Standard 2.0. Tüm render işlemleri IronPDF'in katıştırılmış Chromium motorunu kullanır — başsız tarayıcı bağımlılıkları veya harici hizmetler gerektirmez.
  • Bu yaklaşımı ne zaman kullanmalısınız: Seri yürütmenin izin verdiğinden daha fazla PDF işlemek gerektiğinde — ölçekli belge göçü, dar zaman dilimlerine sahip planlı toplu işler veya değişken belge yükleri ile çok kiracılı platformlar.
  • Teknik olarak neden önemlidir: IronPDF'in ChromePdfRenderer thread-safe ve her render için durumdan bağımsızdır; bu da birden fazla thread'in güvenle tek bir render örneğini paylaşmasını sağlar. .NET'in Görev Paralel Kütüphanesi ile IDisposable, PdfDocument üzerinde birleştirildiğinde, yarış koşulları veya bellek sızıntıları olmadan öngörülebilir bellek davranışı ve CPU doygunluğu elde edersiniz.

Bir dizininin tamamını HTML dosyalarını sadece birkaç satır kodla PDF'ye dönüştürün:

  1. NuGet Paket Yöneticisi ile https://www.nuget.org/packages/IronPdf yükleyin

    PM > Install-Package IronPdf
  2. Bu kod parçasını kopyalayıp çalıştırın.

    using IronPdf;
    using System.IO;
    using System.Threading.Tasks;
    
    var renderer = new ChromePdfRenderer();
    var htmlFiles = Directory.GetFiles("input/", "*.html");
    
    Parallel.ForEach(htmlFiles, htmlFile =>
    {
        var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
        pdf.SaveAs($"output/{Path.GetFileNameWithoutExtension(htmlFile)}.pdf");
    });
  3. Canlı ortamınızda test etmek için dağıtın

    Bugün projenizde IronPDF kullanmaya başlayın ücretsiz deneme ile

    arrow pointer

IronPDF'nin 30 günlük deneme sürümünü satın aldıktan veya kaydolduktan sonra, uygulamanızın başlangıcında lisans anahtarınızı ekleyin.

IronPdf.License.LicenseKey = "KEY";
IronPdf.License.LicenseKey = "KEY";
Imports IronPdf

IronPdf.License.LicenseKey = "KEY"
$vbLabelText   $csharpLabel

!{--0100110001001001010000100101001001000001010100100101100101011111--}

NuGet NuGet ile Yükle

PM >  Install-Package IronPdf

IronPDF üzerine NuGet için hızlı bir kurulum kontrol edin. 10 milyondan fazla indirme ile C# ile PDF geliştirmesini dönüştürüyor. DLL veya Windows yükleyicisini de indirebilirsiniz.

İçindekiler


Binlerce PDF İşlemek Zorunda Olduğunuzda

Toplu PDF işleme, niş bir gereklilik değil — kurumsal belge yönetiminin rutin bir parçasıdır. Bunu gerektiren senaryolar her endüstride ortaya çıkmakta ve ortak bir özelliği paylaşmaktadır: işleri birer birer yapmak bir seçenek değildir.

Belge taşıma projeleri en yaygın tetikleyicilerden biridir. Bir kuruluş bir belge yönetim sisteminden başka bir sisteme geçtiğinde, binlerce (bazen milyonlarca) belge dönüştürülmeli, yeniden biçimlendirilmeli veya yeniden etiketlenmelidir. Eski bir talep sisteminden göç eden bir sigorta şirketi, 500.000 TIFF tabanlı talep belgesini aranabilir PDF'lere dönüştürmek zorunda kalabilir. Yeni bir dava yönetimi platformuna geçiş yapan bir hukuk firması, dağınık yazışmaları birleştirerek tek bir dava dosyası haline getirmek zorunda kalabilir. Bunlar bir sefere mahsus işlerdir, ancak kapsam bakımından devasa ve hataların affedilmez olduğu işlerdir.

Günlük rapor oluşturma bu problemin sürekli hali olan versiyonudur. Binlerce müşteri için gün sonu portföy raporları üreten finansal kurumlar, her outbound konteynırı için gönderi manifesoları yaratan lojistik firmalar, yüzlerce bölümde günlük hasta özetleri oluşturan sağlık sistemleri — tümü, ardışık işlem yapmanın kabul edilebilir zaman pencerelerini aşacağı bir ölçekte PDF çıktısı üretmektedir. Saat 6'da hazır olması gereken 10.000 rapor olduğunda ve veriler gece yarısına kadar tamamlanmadığında, bunları teker teker oluşturmak için altı saatiniz yoktur.

Arşiv dijitalleştirme, taşıma ve uyumluluğun kesişim noktasında yer alır. On yıllık kağıt kayıtlarına sahip hükümet ajansları, üniversiteler ve şirketler, belgeleri standartlara uygun formatlarda (genellikle PDF/A) dijitalleştirip arşivlemeleri için zorunluluklarla karşı karşıya kalmaktadır. Çok büyük hacimler söz konusudur — sadece NARA, kalıcı koruma için milyonlarca federal kayıt sayfası almaktadır — ve süreç, yıllar sonra boşluklar keşfetmemeniz için yeterince güvenilir olmalıdır.

Uyum iyileştirme genellikle en acil tetikleyicidir. Bir denetim, belge arşivinizin yeni yürürlüğe giren bir standardı karşılamadığını ortaya çıkardığında — örneğin, saklanan faturalarınız e-faturalaşma düzenlemeleri için PDF/A-3 ile uyumlu değilse veya tıbbi kayıtlarınız 508. Bölüm tarafından gereken erişilebilirlik etiketini taşımıyorsa — mevcut arşivinizin tamamını yeni standarda göre işlemelisiniz. Baskı yüksektir, zaman çizelgesi sıkıdır ve hacim, arşivinizin içeriği kadardır.

Bu senaryoların her birinde temel zorluk aynıdır: çok sayıda PDF işlemini güvenilir, verimli ve belleği tüketmeden veya bir şeyler ters gittiğinde yarım kalan işler bırakmadan nasıl işlersiniz?

Dört toplu işlem senaryosunu gösteren infografik — Belge Göçü, Günlük Rapor Üretimi, Arşiv Sayısallaştırma, Uyumluluk İyileştirme — her biri bir simge, tipik hacim aralığı ve zaman baskısı göstergesi


IronPDF Toplu İşleme Mimarisi

Belirli işlemlerle başlamadan önce, IronPDF'in eşzamanlı iş yüklerini nasıl ele aldığı ve üzerine bir toplu iş hattı inşa ederken hangi mimari kararları almanız gerektiğini anlamak önemlidir.

IronPDF Kurulumu

IronPDF'i NuGet aracılığıyla yükleyin:

Install-Package IronPdf
Install-Package IronPdf
SHELL

.NET CLI kullanarak:

dotnet add package IronPdf
dotnet add package IronPdf
SHELL

IronPDF, .NET Framework 4.6.2+, .NET Core, .NET 5 ile .NET 10 ve .NET Standard 2.0'ı destekler. Windows, Linux, macOS ve Docker konteynırlarında çalışır, hem yerinde toplu işleri hem de bulut yerli dağıtımı için uygundur.

Üretim toplu işlemi için, herhangi bir PDF işlemi başlamadan önce uygulama başlangıcında License.LicenseKey ile lisans anahtarınızı ayarlayın. Bu, tüm iş parçacıklarındaki her bir oluşturma çağrısının, dosya başına filigran olmadan, tam özellik setine erişebilmesini sağlar.


Eşzamanlılık Kontrolü ve İş Parçacığı Güvenliği

IronPDF'in Chromium tabanlı oluşturma motoru, iş parçacığı güvenliğindedir. Farklı thread'ler arasında birden fazla ChromePdfRenderer örneği oluşturabilir veya tek bir örneği paylaşabilirsiniz — IronPDF iç senkronizasyonu halleder. Toplu işlem için resmi öneri, iş yükünü otomatik olarak tüm kullanılabilir CPU çekirdeklerine dağıtan .NET'in yerleşik Parallel.ForEach kullanmaktır.

Ancak, 'thread-safe' ifadesi 'sınırsız thread kullanımı' anlamına gelmez. Her eş zamanlı PDF işleme işlemi bellek tüketir (Chromium motorunun DOM ayrıştırma, CSS konumlandırma ve resim rasterizasyonu için çalışma alanına ihtiyaçı vardır) ve bellekle sınırlı bir sistemde çok fazla paralel işlem başlatmak performansı düşürür veya OutOfMemoryException neden olur. Doğru eşzamanlılık seviyesi donanımınıza bağlıdır: 64 GB RAM'e sahip 16 çekirdekli bir sunucu, 8-12 eşzamanlı render işlemine rahatlıkla dayanabilir; 8 GB belleğe sahip 4 çekirdekli bir VM 2–4 ile sınırlı olabilir. Bunu ParallelOptions.MaxDegreeOfParallelism ile kontrol edin — başlangıç noktasında kullanılabilir CPU çekirdeklerinizin yaklaşık yarısını ayarlayın ve daha sonra gözlemlenen bellek baskısına göre ayarlayın.

Büyük Ölçekli Bellek Yönetimi

Bellek yönetimi, toplu PDF işleme için en önemli konudur. Her PdfDocument nesnesi bir PDF'nin tam ikili içeriğini bellekte tutar ve bu nesneleri kapatmayı başaramamak, işlenen dosya sayısıyla birlikte belleğin doğrusal olarak büyümesine neden olur.

Kritik kural: her zaman using ifadeleri kullanın veya Dispose() nesneleri üzerinde açıkça çağırın. IronPDF'in PdfDocument sınıfı IDisposable uygular ve bu sınıfın kapatılmaması, toplu senaryolarda bellek sorunlarının en yaygın nedenidir. İşleme döngünüzün her tekrarı bir PdfDocument oluşturmalı, işini yapmalı ve kapatmalıdır — belirli bir nedeniniz ve bunu yönetebilecek yeterli belleğiniz yoksa PdfDocument nesnelerini bir liste veya koleksiyonda biriktirmeyin.

Boşaltmanın ötesinde, büyük partiler için şu bellek yönetim stratejilerini düşünün:

Parçalar halinde işleyin, hepsini bir kerede yüklemek yerine. 50.000 dosya işlemeniz gerektiğinde, hepsini bir listeye sıralamanıza ve sonra yinelemenize gerek yok — bunların 100 veya 500'lük partiler halinde işlenmesine izin verin, çöp toplayıcının parçalar arasında bellek geri kazanmasına izin verin.

Aşırı büyük partiler için parçalar arasında çöp toplama zorlayin. Genel olarak ÇG'nin kendini yönetmesine izin vermelisiniz, ancak toplu işleme, veri aralıkları arasında GC.Collect() çağırmanın bellek baskısının birikmesini önleyebileceği nadir senaryolardan biridir.

Bellek tüketimini izleyin GC.GetTotalMemory() veya işlem düzeyindeki metrikler kullanarak. Eğer bellek kullanımı bir eşit değeri (örnek: mevcut RAM'in %80'i) geçerse, işlemeye ara verin ve GC'nin yetişmesine izin verin.


İlerleme Raporlama ve Kayıt

Bir toplu iş bazen saatlerce sürebilir, böyle bir durumda ilerlemeyi görmek opsiyonel değil — gereklidir. En azından, her dosyanın başlangıç ve tamamlama sürecini kaydetmelisiniz, başarı/başarısızlık sayısını takip etmeli ve kalan tahmini zamanı belirlemelisiniz. Paralel işlemleri çalıştırırken thread-safe sayaçlar için Interlocked.Increment kullanın ve çıktınızı aşırı doldurmamak için düzenli aralıklarla (her 50 veya 100 dosyada) günlük yapın. Geçen sürenizi System.Diagnostics.Stopwatch ile takip edin ve anlamlı bir TAÇ vermek için bir saniyedeki dosya oranı hesaplayın.

Üretim toplu işleri için, ilerlemeyi dayanıklı bir depoya (veritabanı, dosya veya mesaj sırası) yazmayı düşünün, böylece izleme panoları toplu işlemeye doğrudan bağlanmadan gerçek zamanlı durum gösterebilir.


Yaygın Toplu İşlemler

Mimari temeli oluşturduktan sonra, en yaygın toplu işlemlerle ve bunların IronPDF uygulamalarını inceleyelim.

Toplu HTML'den PDF'ye Dönüştürme

HTML'den PDF'ye dönüştürme en yaygın toplu işlemidir. İster şablonlardan fatura oluşturuyor olun, ister HTML belgeleri kütüphanesini PDF'ye çeviriyorsanız, ister bir web uygulamasından dinamik raporlar render ediyorsanız, desen aynıdır: girdilerinizi yineleyerek her birini işleyin ve çıktısını kaydedin.

Girdi (5 HTML Dosyası)

HTML Invoice INV-2026-001
HTML Invoice INV-2026-002
HTML Invoice INV-2026-003
HTML Invoice INV-2026-004
HTML Invoice INV-2026-005

Gerçekleştirilmesi, tüm HTML dosyalarını eş zamanlı olarak işlemek için ChromePdfRenderer ve Parallel.ForEach kullanır, paralellik, bellek tüketimi ile erişim dengesi sağlamak için MaxDegreeOfParallelism üzerinden kontrol edilir. Her dosya, thread-safe Interlocked sayaçları üzerinden ilerleme izleme ile RenderHtmlFileAsPdf kullanılarak işlenir ve çıktı dizinine kaydedilir.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-html-to-pdf.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;

// Configure paths
string inputFolder = "input/";
string outputFolder = "output/";

Directory.CreateDirectory(outputFolder);

string[] htmlFiles = Directory.GetFiles(inputFolder, "*.html");
Console.WriteLine($"Found {htmlFiles.Length} HTML files to convert");

// Create renderer instance (thread-safe, can be shared)
var renderer = new ChromePdfRenderer();

// Track progress
int processed = 0;
int failed = 0;

// Process in parallel with controlled concurrency
var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(htmlFiles, options, htmlFile =>
{
    try
    {
        string fileName = Path.GetFileNameWithoutExtension(htmlFile);
        string outputPath = Path.Combine(outputFolder, $"{fileName}.pdf");

        using var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
        pdf.SaveAs(outputPath);

        Interlocked.Increment(ref processed);
        Console.WriteLine($"[OK] {fileName}.pdf");
    }
    catch (Exception ex)
    {
        Interlocked.Increment(ref failed);
        Console.WriteLine($"[ERROR] {Path.GetFileName(htmlFile)}: {ex.Message}");
    }
});

Console.WriteLine($"\nComplete: {processed} succeeded, {failed} failed");
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Threading

' Configure paths
Dim inputFolder As String = "input/"
Dim outputFolder As String = "output/"

Directory.CreateDirectory(outputFolder)

Dim htmlFiles As String() = Directory.GetFiles(inputFolder, "*.html")
Console.WriteLine($"Found {htmlFiles.Length} HTML files to convert")

' Create renderer instance (thread-safe, can be shared)
Dim renderer As New ChromePdfRenderer()

' Track progress
Dim processed As Integer = 0
Dim failed As Integer = 0

' Process in parallel with controlled concurrency
Dim options As New ParallelOptions With {
    .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
}

Parallel.ForEach(htmlFiles, options, Sub(htmlFile)
    Try
        Dim fileName As String = Path.GetFileNameWithoutExtension(htmlFile)
        Dim outputPath As String = Path.Combine(outputFolder, $"{fileName}.pdf")

        Using pdf = renderer.RenderHtmlFileAsPdf(htmlFile)
            pdf.SaveAs(outputPath)
        End Using

        Interlocked.Increment(processed)
        Console.WriteLine($"[OK] {fileName}.pdf")
    Catch ex As Exception
        Interlocked.Increment(failed)
        Console.WriteLine($"[ERROR] {Path.GetFileName(htmlFile)}: {ex.Message}")
    End Try
End Sub)

Console.WriteLine($"\nComplete: {processed} succeeded, {failed} failed")
$vbLabelText   $csharpLabel

Çıktı


Her HTML fatura karşılık gelen bir PDF'ye dönüşür. Yukarıdaki INV-2026-001.pdf — 5 toplu çıktıdan biri.

Şablon tabanlı oluşturmada (örnek: faturalar, raporlar), genellikle verileri HTML şablonuna birleştirirsiniz ve sonra render edersiniz. Yaklaşım basittir: HTML şablonunuzu bir kez yükleyin, kayıtlara özel verileri (müşteri adı, toplamlar, tarihler) enjekte etmek için string.Replace kullanın ve dolu HTML'yi paralel döngünüz içinde RenderHtmlAsPdf'ya iletin. IronPDF, Parallel.ForEach yerine async/await kullanmak istediğiniz senaryolar için de RenderHtmlAsPdfAsync sağlar — async desenlerini detaylı olarak bir sonraki bölümde ele alacağız.


Toplu PDF Birleşimi

PDF'lerin birleşik belgelere birleştirilmesi, yargısal (dava dosyası belgelerini birleştirme), finansal (aylık beyanları üç aylık raporlara birleştirme) ve yayın iş akışlarında yaygındır.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-merge.cs
using IronPdf;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

string inputFolder = "documents/";
string outputFolder = "merged/";

Directory.CreateDirectory(outputFolder);

// Group PDFs by prefix (e.g., "invoice-2026-01-*.pdf" -> one merged file)
var pdfFiles = Directory.GetFiles(inputFolder, "*.pdf");
var groups = pdfFiles
    .GroupBy(f => Path.GetFileName(f).Split('-').Take(3).Aggregate((a, b) => $"{a}-{b}"))
    .Where(g => g.Count() > 1);

Console.WriteLine($"Found {groups.Count()} groups to merge");

foreach (var group in groups)
{
    string groupName = group.Key;
    var filesToMerge = group.OrderBy(f => f).ToList();

    Console.WriteLine($"Merging {filesToMerge.Count} files into {groupName}.pdf");

    try
    {
        // Load all PDFs for this group
        var pdfDocs = new List<PdfDocument>();
        foreach (string filePath in filesToMerge)
        {
            pdfDocs.Add(PdfDocument.FromFile(filePath));
        }

        // Merge all documents
        using var merged = PdfDocument.Merge(pdfDocs);
        merged.SaveAs(Path.Combine(outputFolder, $"{groupName}-merged.pdf"));

        // Dispose source documents
        foreach (var doc in pdfDocs)
        {
            doc.Dispose();
        }

        Console.WriteLine($"  [OK] Created {groupName}-merged.pdf ({merged.PageCount} pages)");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"  [ERROR] {groupName}: {ex.Message}");
    }
}

Console.WriteLine("\nMerge complete");
Imports IronPdf
Imports System
Imports System.IO
Imports System.Linq
Imports System.Collections.Generic

Module Program
    Sub Main()
        Dim inputFolder As String = "documents/"
        Dim outputFolder As String = "merged/"

        Directory.CreateDirectory(outputFolder)

        ' Group PDFs by prefix (e.g., "invoice-2026-01-*.pdf" -> one merged file)
        Dim pdfFiles = Directory.GetFiles(inputFolder, "*.pdf")
        Dim groups = pdfFiles _
            .GroupBy(Function(f) Path.GetFileName(f).Split("-"c).Take(3).Aggregate(Function(a, b) $"{a}-{b}")) _
            .Where(Function(g) g.Count() > 1)

        Console.WriteLine($"Found {groups.Count()} groups to merge")

        For Each group In groups
            Dim groupName As String = group.Key
            Dim filesToMerge = group.OrderBy(Function(f) f).ToList()

            Console.WriteLine($"Merging {filesToMerge.Count} files into {groupName}.pdf")

            Try
                ' Load all PDFs for this group
                Dim pdfDocs As New List(Of PdfDocument)()
                For Each filePath As String In filesToMerge
                    pdfDocs.Add(PdfDocument.FromFile(filePath))
                Next

                ' Merge all documents
                Using merged = PdfDocument.Merge(pdfDocs)
                    merged.SaveAs(Path.Combine(outputFolder, $"{groupName}-merged.pdf"))
                End Using

                ' Dispose source documents
                For Each doc In pdfDocs
                    doc.Dispose()
                Next

                Console.WriteLine($"  [OK] Created {groupName}-merged.pdf ({merged.PageCount} pages)")
            Catch ex As Exception
                Console.WriteLine($"  [ERROR] {groupName}: {ex.Message}")
            End Try
        Next

        Console.WriteLine(vbCrLf & "Merge complete")
    End Sub
End Module
$vbLabelText   $csharpLabel

Çok sayıda dosyanın birleştirilmesi söz konusu olduğunda, bellek konusunda dikkatli olun: PdfDocument.Merge yöntemi kaynak belgelerin hepsini aynı anda belleğe yükler. Yüzlerce büyük PDF'i birleştirecekseniz aşamalar halinde birleşmeyi düşünün — 10-20 dosyalık grupları ara belgelere birleştirin, sonra bunları birleştirin.

Toplu PDF Ayrımı

Çok sayfalı PDF'lerin tek sayfalar (veya sayfa aralıkları) haline getirilmesi, birleşimin tersidir. Mailroom işleminde yaygındır, burada taranmış bir yazılar paketinin ayrı kayıtlara ayrılması ve bileşik belgelerin ayrılması gereken baskı iş akışlarında yaygındır.

Giriş

Aşağıdaki kod, paralel bir döngüde CopyPage kullanarak her sayfayı ayrı dosyalara çıkarma işlemini göstermektedir. Alternatif bir SplitByRange yardımcı işlevi, sayfa aralığı çıkarmanın nasıl yapılacağını gösterir; bu, büyük belgeleri daha küçük segmentlere ayırmak için yararlıdır.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-split.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;

string inputFolder = "multipage/";
string outputFolder = "split/";

Directory.CreateDirectory(outputFolder);

string[] pdfFiles = Directory.GetFiles(inputFolder, "*.pdf");
Console.WriteLine($"Found {pdfFiles.Length} PDFs to split");

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(pdfFiles, options, pdfFile =>
{
    string baseName = Path.GetFileNameWithoutExtension(pdfFile);

    try
    {
        using var pdf = PdfDocument.FromFile(pdfFile);
        int pageCount = pdf.PageCount;

        Console.WriteLine($"Splitting {baseName}.pdf ({pageCount} pages)");

        // Extract each page as a separate PDF
        for (int i = 0; i < pageCount; i++)
        {
            using var singlePage = pdf.CopyPage(i);
            string outputPath = Path.Combine(outputFolder, $"{baseName}-page-{i + 1:D3}.pdf");
            singlePage.SaveAs(outputPath);
        }

        Console.WriteLine($"  [OK] Created {pageCount} files from {baseName}.pdf");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"  [ERROR] {baseName}: {ex.Message}");
    }
});

// Alternative: Extract page ranges instead of individual pages
void SplitByRange(string inputFile, string outputFolder, int pagesPerChunk)
{
    using var pdf = PdfDocument.FromFile(inputFile);
    string baseName = Path.GetFileNameWithoutExtension(inputFile);
    int totalPages = pdf.PageCount;
    int chunkNumber = 1;

    for (int startPage = 0; startPage < totalPages; startPage += pagesPerChunk)
    {
        int endPage = Math.Min(startPage + pagesPerChunk - 1, totalPages - 1);
        using var chunk = pdf.CopyPages(startPage, endPage);
        chunk.SaveAs(Path.Combine(outputFolder, $"{baseName}-chunk-{chunkNumber:D3}.pdf"));
        chunkNumber++;
    }
}

Console.WriteLine("\nSplit complete");
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks

Module Program
    Sub Main()
        Dim inputFolder As String = "multipage/"
        Dim outputFolder As String = "split/"

        Directory.CreateDirectory(outputFolder)

        Dim pdfFiles As String() = Directory.GetFiles(inputFolder, "*.pdf")
        Console.WriteLine($"Found {pdfFiles.Length} PDFs to split")

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(pdfFiles, options, Sub(pdfFile)
                                                Dim baseName As String = Path.GetFileNameWithoutExtension(pdfFile)

                                                Try
                                                    Using pdf = PdfDocument.FromFile(pdfFile)
                                                        Dim pageCount As Integer = pdf.PageCount

                                                        Console.WriteLine($"Splitting {baseName}.pdf ({pageCount} pages)")

                                                        ' Extract each page as a separate PDF
                                                        For i As Integer = 0 To pageCount - 1
                                                            Using singlePage = pdf.CopyPage(i)
                                                                Dim outputPath As String = Path.Combine(outputFolder, $"{baseName}-page-{i + 1:D3}.pdf")
                                                                singlePage.SaveAs(outputPath)
                                                            End Using
                                                        Next

                                                        Console.WriteLine($"  [OK] Created {pageCount} files from {baseName}.pdf")
                                                    End Using
                                                Catch ex As Exception
                                                    Console.WriteLine($"  [ERROR] {baseName}: {ex.Message}")
                                                End Try
                                            End Sub)

        ' Alternative: Extract page ranges instead of individual pages
        Sub SplitByRange(inputFile As String, outputFolder As String, pagesPerChunk As Integer)
            Using pdf = PdfDocument.FromFile(inputFile)
                Dim baseName As String = Path.GetFileNameWithoutExtension(inputFile)
                Dim totalPages As Integer = pdf.PageCount
                Dim chunkNumber As Integer = 1

                For startPage As Integer = 0 To totalPages - 1 Step pagesPerChunk
                    Dim endPage As Integer = Math.Min(startPage + pagesPerChunk - 1, totalPages - 1)
                    Using chunk = pdf.CopyPages(startPage, endPage)
                        chunk.SaveAs(Path.Combine(outputFolder, $"{baseName}-chunk-{chunkNumber:D3}.pdf"))
                        chunkNumber += 1
                    End Using
                Next
            End Using
        End Sub

        Console.WriteLine(vbCrLf & "Split complete")
    End Sub
End Module
$vbLabelText   $csharpLabel

Çıktı


Sayfa 2, tek basına bir PDF olarak elde edilmiştir (annual-report-page-2.pdf)

IronPDF'in CopyPage ve CopyPages yöntemleri, belirtilen sayfaları içeren yeni PdfDocument nesneleri oluşturur. Hem kaynak belgeleri hem de kaydedilen her sayfa belgesini kaydettikten sonra boşaltmayı unutmayın.


Toplu Sıkıştırma

Depolama maliyetleri önemliyse veya kısıtlı bant genişlikli bağlantılar üzerinden PDF'leri iletmeniz gerekiyorsa, toplu sıkıştırma arşiv ayağınızı ciddi şekilde azaltabilir. IronPDF, iki sıkıştırma yaklaşımı sağlar: görüntü kalitesi/boyutu düşürme için CompressImages ve yapısal meta veriyi kaldırma için CompressStructTree. 2025.12 sürümünde tanıtılan yeni CompressAndSaveAs API'si, birden fazla optimizasyon tekniğini birleştirerek üstün sıkıştırma sağlar.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-compression.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;

string inputFolder = "originals/";
string outputFolder = "compressed/";

Directory.CreateDirectory(outputFolder);

string[] pdfFiles = Directory.GetFiles(inputFolder, "*.pdf");
Console.WriteLine($"Found {pdfFiles.Length} PDFs to compress");

long totalOriginalSize = 0;
long totalCompressedSize = 0;
int processed = 0;

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(pdfFiles, options, pdfFile =>
{
    string fileName = Path.GetFileName(pdfFile);
    string outputPath = Path.Combine(outputFolder, fileName);

    try
    {
        long originalSize = new FileInfo(pdfFile).Length;
        Interlocked.Add(ref totalOriginalSize, originalSize);

        using var pdf = PdfDocument.FromFile(pdfFile);

        // Apply compression with JPEG quality setting (0-100, lower = more compression)
        pdf.CompressAndSaveAs(outputPath, 60);

        long compressedSize = new FileInfo(outputPath).Length;
        Interlocked.Add(ref totalCompressedSize, compressedSize);
        Interlocked.Increment(ref processed);

        double reduction = (1 - (double)compressedSize / originalSize) * 100;
        Console.WriteLine($"[OK] {fileName}: {originalSize / 1024}KB → {compressedSize / 1024}KB ({reduction:F1}% reduction)");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
    }
});

double totalReduction = (1 - (double)totalCompressedSize / totalOriginalSize) * 100;
Console.WriteLine($"\nCompression complete:");
Console.WriteLine($"  Files processed: {processed}");
Console.WriteLine($"  Total original: {totalOriginalSize / 1024 / 1024}MB");
Console.WriteLine($"  Total compressed: {totalCompressedSize / 1024 / 1024}MB");
Console.WriteLine($"  Overall reduction: {totalReduction:F1}%");
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Threading

Module Program
    Sub Main()
        Dim inputFolder As String = "originals/"
        Dim outputFolder As String = "compressed/"

        Directory.CreateDirectory(outputFolder)

        Dim pdfFiles As String() = Directory.GetFiles(inputFolder, "*.pdf")
        Console.WriteLine($"Found {pdfFiles.Length} PDFs to compress")

        Dim totalOriginalSize As Long = 0
        Dim totalCompressedSize As Long = 0
        Dim processed As Integer = 0

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(pdfFiles, options, Sub(pdfFile)
                                                Dim fileName As String = Path.GetFileName(pdfFile)
                                                Dim outputPath As String = Path.Combine(outputFolder, fileName)

                                                Try
                                                    Dim originalSize As Long = New FileInfo(pdfFile).Length
                                                    Interlocked.Add(totalOriginalSize, originalSize)

                                                    Using pdf = PdfDocument.FromFile(pdfFile)
                                                        ' Apply compression with JPEG quality setting (0-100, lower = more compression)
                                                        pdf.CompressAndSaveAs(outputPath, 60)
                                                    End Using

                                                    Dim compressedSize As Long = New FileInfo(outputPath).Length
                                                    Interlocked.Add(totalCompressedSize, compressedSize)
                                                    Interlocked.Increment(processed)

                                                    Dim reduction As Double = (1 - CDbl(compressedSize) / originalSize) * 100
                                                    Console.WriteLine($"[OK] {fileName}: {originalSize \ 1024}KB → {compressedSize \ 1024}KB ({reduction:F1}% reduction)")
                                                Catch ex As Exception
                                                    Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                                End Try
                                            End Sub)

        Dim totalReduction As Double = (1 - CDbl(totalCompressedSize) / totalOriginalSize) * 100
        Console.WriteLine(vbCrLf & "Compression complete:")
        Console.WriteLine($"  Files processed: {processed}")
        Console.WriteLine($"  Total original: {totalOriginalSize \ 1024 \ 1024}MB")
        Console.WriteLine($"  Total compressed: {totalCompressedSize \ 1024 \ 1024}MB")
        Console.WriteLine($"  Overall reduction: {totalReduction:F1}%")
    End Sub
End Module
$vbLabelText   $csharpLabel

Sıkıştırmayla ilgili aklınızda bulundurmanız gereken birkaç şey: 60'ın altındaki JPEG kalite ayarları çoğu görüntüde görünür artefaktlar oluşturur. ShrinkImage seçeneği bazı yapılandırmalarda bozulmaya neden olabilir — tam bir batch çalıştırmadan önce temsilci örneklerle test yapın. Ve yapı ağacını (CompressStructTree) kaldırmak, sıkıştırılmış PDF'lerde metin seçimi ve aramayı etkileyecektir, bu yüzden yalnızca bu özelliklere ihtiyaç duymadığınız durumlarda kullanın.


Toplu Biçim Dönüşümü (PDF/A, PDF/UA)

Mevcut bir arsivi standartlara uygun bir formata dönüştürmek — uzun vade arşivleme için PDF/A veya erişilebilirlik için PDF/UA — en yüksek değerli toplu işlemlerden biridir. IronPDF, PDF/A sürümlerinin tam yelpazesini (PDF/A-4 dahil, 2025.11 sürümüne eklenmiş) ve PDF/UA uyumluluğunu (PDF/UA-2 dahil, 2025.12 sürümüne eklenmiş) destekler.

Giriş

Örnek, her PDF'yi PdfDocument.FromFile ile yükler, ardından SaveAsPdfA ile PdfAVersions.PdfA3b parametresi kullanarak PDF/A-3b'ye dönüştürür. Alternatif ConvertToPdfUA işlevi, SaveAsPdfUA kullanarak erişilebilirlik uyumluluk dönüşümünü gösterir, ancak PDF/UA uygun yapısal etiketlemeye sahip kaynak belgeler gerektirir.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-format-conversion.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;

string inputFolder = "originals/";
string outputFolder = "pdfa-archive/";

Directory.CreateDirectory(outputFolder);

string[] pdfFiles = Directory.GetFiles(inputFolder, "*.pdf");
Console.WriteLine($"Found {pdfFiles.Length} PDFs to convert to PDF/A-3b");

int converted = 0;
int failed = 0;

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(pdfFiles, options, pdfFile =>
{
    string fileName = Path.GetFileName(pdfFile);
    string outputPath = Path.Combine(outputFolder, fileName);

    try
    {
        using var pdf = PdfDocument.FromFile(pdfFile);

        // Convert to PDF/A-3b for long-term archival
        pdf.SaveAsPdfA(outputPath, PdfAVersions.PdfA3b);

        Interlocked.Increment(ref converted);
        Console.WriteLine($"[OK] {fileName} → PDF/A-3b");
    }
    catch (Exception ex)
    {
        Interlocked.Increment(ref failed);
        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
    }
});

Console.WriteLine($"\nConversion complete: {converted} succeeded, {failed} failed");

// Alternative: Convert to PDF/UA for accessibility compliance
void ConvertToPdfUA(string inputFolder, string outputFolder)
{
    Directory.CreateDirectory(outputFolder);
    string[] files = Directory.GetFiles(inputFolder, "*.pdf");

    Parallel.ForEach(files, pdfFile =>
    {
        string fileName = Path.GetFileName(pdfFile);
        try
        {
            using var pdf = PdfDocument.FromFile(pdfFile);

            // PDF/UA requires proper tagging - ensure source is well-structured
            pdf.SaveAsPdfUA(Path.Combine(outputFolder, fileName));
            Console.WriteLine($"[OK] {fileName} → PDF/UA");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
        }
    });
}
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Threading

Module Program
    Sub Main()
        Dim inputFolder As String = "originals/"
        Dim outputFolder As String = "pdfa-archive/"

        Directory.CreateDirectory(outputFolder)

        Dim pdfFiles As String() = Directory.GetFiles(inputFolder, "*.pdf")
        Console.WriteLine($"Found {pdfFiles.Length} PDFs to convert to PDF/A-3b")

        Dim converted As Integer = 0
        Dim failed As Integer = 0

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(pdfFiles, options, Sub(pdfFile)
                                                Dim fileName As String = Path.GetFileName(pdfFile)
                                                Dim outputPath As String = Path.Combine(outputFolder, fileName)

                                                Try
                                                    Using pdf = PdfDocument.FromFile(pdfFile)
                                                        ' Convert to PDF/A-3b for long-term archival
                                                        pdf.SaveAsPdfA(outputPath, PdfAVersions.PdfA3b)

                                                        Interlocked.Increment(converted)
                                                        Console.WriteLine($"[OK] {fileName} → PDF/A-3b")
                                                    End Using
                                                Catch ex As Exception
                                                    Interlocked.Increment(failed)
                                                    Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                                End Try
                                            End Sub)

        Console.WriteLine($"{vbCrLf}Conversion complete: {converted} succeeded, {failed} failed")
    End Sub

    ' Alternative: Convert to PDF/UA for accessibility compliance
    Sub ConvertToPdfUA(inputFolder As String, outputFolder As String)
        Directory.CreateDirectory(outputFolder)
        Dim files As String() = Directory.GetFiles(inputFolder, "*.pdf")

        Parallel.ForEach(files, Sub(pdfFile)
                                    Dim fileName As String = Path.GetFileName(pdfFile)
                                    Try
                                        Using pdf = PdfDocument.FromFile(pdfFile)
                                            ' PDF/UA requires proper tagging - ensure source is well-structured
                                            pdf.SaveAsPdfUA(Path.Combine(outputFolder, fileName))
                                            Console.WriteLine($"[OK] {fileName} → PDF/UA")
                                        End Using
                                    Catch ex As Exception
                                        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                    End Try
                                End Sub)
    End Sub
End Module
$vbLabelText   $csharpLabel

Çıktı

Uyumluluk meta verilerini gösteren PDF/A dönüşüm çıktı karşılaştırması

Çıktı PDF görünümde byte-for-byte özdeş ama şu an arşiv sistemleri için PDF/A-3b uyumluluk metadata taşıyor.

Biçim dönüşümü, mevcut arşivin bir düzenleme standardına uygun olmadığını keşfeden bir kuruluş için özellikle önemlidir. Toplu kalıp basittir, ancak doğrulama adımı kritiktir — her dönüştürülen dosyanın gerçekten uyumluluk kontrollerini geçtiğini kontrol etmeden tamamlanmış saymayın. Doğrulamayı aşağıdaki dayanıklılık bölümünde ayrıntılı olarak ele alıyoruz.


Dayanıklı Toplu İşleme Hatları Oluşturmak

100 dosya üzerinde mükemmel çalışıp, 50.000 dosyanın 4,327. dosyasında çöküş olurken hiçbir faydası yok. Dayanıklılık — hataları zarifçe ele almak, geçici hataları tekrar denemek ve çöktükten sonra devam edebilmek — üretim kalitesindeki bir boru hattını bir prototipten ayırır.

Hata Yönetimi ve Başarısızlıkta Atla

En temel dayaniklilik kalibi başarısızlıkta atlamaktır: tek bir dosya işlenmezse, hatayı kaydedin ve tüm topluluğu iptal etmek yerine bir sonraki dosya ile devam edin. Bu, kulağa bariz geliyor olabilir, ancak Parallel.ForEach kullanırken gözden kaçırılması şaşırtıcı derecede kolaydır — paralel bir görevde işlenmemiş bir istisna, AggregateException ile yayılır ve döngüyü sona erdirir.

Aşağıdaki örnek, hem başarısızlıkta atlama hem de yeniden deneme mantığını birlikte göstermektedir — her dosyayı zarif hata yönetimi için bir try-catch ile sarmalayarak, IOException ve OutOfMemoryException gibi geçici istisnalar için üstel geri çekilmeyle iç bir yeniden deneme döngüsü içermektedir:

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-error-handling-retry.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;

string inputFolder = "input/";
string outputFolder = "output/";
string errorLogPath = "error-log.txt";

Directory.CreateDirectory(outputFolder);

string[] htmlFiles = Directory.GetFiles(inputFolder, "*.html");
var renderer = new ChromePdfRenderer();
var errorLog = new ConcurrentBag<string>();

int processed = 0;
int failed = 0;
int retried = 0;

const int maxRetries = 3;

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(htmlFiles, options, htmlFile =>
{
    string fileName = Path.GetFileNameWithoutExtension(htmlFile);
    string outputPath = Path.Combine(outputFolder, $"{fileName}.pdf");
    int attempt = 0;
    bool success = false;

    while (attempt < maxRetries && !success)
    {
        attempt++;
        try
        {
            using var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
            pdf.SaveAs(outputPath);
            success = true;
            Interlocked.Increment(ref processed);

            if (attempt > 1)
            {
                Interlocked.Increment(ref retried);
                Console.WriteLine($"[OK] {fileName}.pdf (succeeded on attempt {attempt})");
            }
            else
            {
                Console.WriteLine($"[OK] {fileName}.pdf");
            }
        }
        catch (Exception ex) when (IsTransientException(ex) && attempt < maxRetries)
        {
            // Transient error - wait and retry with exponential backoff
            int delayMs = (int)Math.Pow(2, attempt) * 500;
            Console.WriteLine($"[RETRY] {fileName}: {ex.Message} (attempt {attempt}, waiting {delayMs}ms)");
            Thread.Sleep(delayMs);
        }
        catch (Exception ex)
        {
            // Non-transient error or max retries exceeded
            Interlocked.Increment(ref failed);
            string errorMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {fileName} | {ex.GetType().Name} | {ex.Message}";
            errorLog.Add(errorMessage);
            Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
        }
    }
});

// Write error log
if (errorLog.Count > 0)
{
    File.WriteAllLines(errorLogPath, errorLog);
}

Console.WriteLine($"\nBatch complete:");
Console.WriteLine($"  Processed: {processed}");
Console.WriteLine($"  Failed: {failed}");
Console.WriteLine($"  Retried: {retried}");
if (failed > 0)
{
    Console.WriteLine($"  Error log: {errorLogPath}");
}

// Helper to identify transient exceptions worth retrying
bool IsTransientException(Exception ex)
{
    return ex is IOException ||
           ex is OutOfMemoryException ||
           ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase) ||
           ex.Message.Contains("locked", StringComparison.OrdinalIgnoreCase);
}
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading
Imports System.Collections.Concurrent

Module Program
    Sub Main()
        Dim inputFolder As String = "input/"
        Dim outputFolder As String = "output/"
        Dim errorLogPath As String = "error-log.txt"

        Directory.CreateDirectory(outputFolder)

        Dim htmlFiles As String() = Directory.GetFiles(inputFolder, "*.html")
        Dim renderer As New ChromePdfRenderer()
        Dim errorLog As New ConcurrentBag(Of String)()

        Dim processed As Integer = 0
        Dim failed As Integer = 0
        Dim retried As Integer = 0

        Const maxRetries As Integer = 3

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(htmlFiles, options, Sub(htmlFile)
            Dim fileName As String = Path.GetFileNameWithoutExtension(htmlFile)
            Dim outputPath As String = Path.Combine(outputFolder, $"{fileName}.pdf")
            Dim attempt As Integer = 0
            Dim success As Boolean = False

            While attempt < maxRetries AndAlso Not success
                attempt += 1
                Try
                    Using pdf = renderer.RenderHtmlFileAsPdf(htmlFile)
                        pdf.SaveAs(outputPath)
                        success = True
                        Interlocked.Increment(processed)

                        If attempt > 1 Then
                            Interlocked.Increment(retried)
                            Console.WriteLine($"[OK] {fileName}.pdf (succeeded on attempt {attempt})")
                        Else
                            Console.WriteLine($"[OK] {fileName}.pdf")
                        End If
                    End Using
                Catch ex As Exception When IsTransientException(ex) AndAlso attempt < maxRetries
                    ' Transient error - wait and retry with exponential backoff
                    Dim delayMs As Integer = CInt(Math.Pow(2, attempt)) * 500
                    Console.WriteLine($"[RETRY] {fileName}: {ex.Message} (attempt {attempt}, waiting {delayMs}ms)")
                    Thread.Sleep(delayMs)
                Catch ex As Exception
                    ' Non-transient error or max retries exceeded
                    Interlocked.Increment(failed)
                    Dim errorMessage As String = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {fileName} | {ex.GetType().Name} | {ex.Message}"
                    errorLog.Add(errorMessage)
                    Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                End Try
            End While
        End Sub)

        ' Write error log
        If errorLog.Count > 0 Then
            File.WriteAllLines(errorLogPath, errorLog)
        End If

        Console.WriteLine($"{vbCrLf}Batch complete:")
        Console.WriteLine($"  Processed: {processed}")
        Console.WriteLine($"  Failed: {failed}")
        Console.WriteLine($"  Retried: {retried}")
        If failed > 0 Then
            Console.WriteLine($"  Error log: {errorLogPath}")
        End If
    End Sub

    ' Helper to identify transient exceptions worth retrying
    Function IsTransientException(ex As Exception) As Boolean
        Return TypeOf ex Is IOException OrElse
               TypeOf ex Is OutOfMemoryException OrElse
               ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase) OrElse
               ex.Message.Contains("locked", StringComparison.OrdinalIgnoreCase)
    End Function
End Module
$vbLabelText   $csharpLabel

Toplu işlem tamamlandıktan sonra hata günlüklerini inceleyerek hangi dosyaların başarısız olduğunu ve nedenini anlayın. Yaygın hata nedenleri bozulmuş kaynak dosyalar, şifre korumalı PDF'ler, kaynak içerikteki desteklenmeyen özellikler ve çok büyük dokümanlarda bellek sınırlarıdır.

Geçici Hatalar için Geri Dönüş Mantığı

Bazı hatalar geçicidir — tekrar denerseniz başarılı olur. Bunlar, dosya sistemi çekişmesi (başka bir işlem dosyayı kilitli tuttuğunda), geçici bellek baskısı (GC henüz yetişmemiş) ve HTML içeriğinde dış kaynaklar yüklenirken ağ zaman aşımı gibi durumları içerir. Yukarıdaki kod örneği, bu durumlarda üstel geri dönüş ile başa çıkmaktadır — kısa bir bekleme ile başlayıp, her geri dönüş denemesinde ikiye katlayarak, maksimum bir geri deneme sayısında (genellikle 3) sınırlı.

Anahtar, geridönmeye uygun ve olmayan hata çeşitlerini ayırmaktır. Bir IOException (dosya kilitli) veya OutOfMemoryException (geçici baskı) yeniden denemeye değer. Bir ArgumentException (geçersiz giriş) veya tutarlı bir işleme hatası değil — yeniden denemek işe yaramaz ve zaman ile kaynak israf eder.


Çok Saatlik Kullanımdan Sonra Kesilme Durumunda Geri Başlama

Bir toplu işlemin 50.000 dosyayı birkaç saatte işlediğinde, 35.000. dosyadaki bir çöküş başlangıçtan yeniden başlamak anlamına gelmemelidir. Kayıt noktası alma — hangi dosyaların başarıyla işlendiğini kayıt etmek — kaldığınız yerden devam etmenizi sağlar.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-checkpointing.cs
using IronPdf;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;

string inputFolder = "input/";
string outputFolder = "output/";
string checkpointPath = "checkpoint.txt";
string errorLogPath = "errors.txt";

Directory.CreateDirectory(outputFolder);

// Load checkpoint - files already processed successfully
var completedFiles = new HashSet<string>();
if (File.Exists(checkpointPath))
{
    completedFiles = new HashSet<string>(File.ReadAllLines(checkpointPath));
    Console.WriteLine($"Resuming from checkpoint: {completedFiles.Count} files already processed");
}

// Get files to process (excluding already completed)
string[] allFiles = Directory.GetFiles(inputFolder, "*.html");
string[] filesToProcess = allFiles
    .Where(f => !completedFiles.Contains(Path.GetFileName(f)))
    .ToArray();

Console.WriteLine($"Files to process: {filesToProcess.Length} (skipping {completedFiles.Count} already done)");

var renderer = new ChromePdfRenderer();
var checkpointLock = new object();
int processed = 0;
int failed = 0;

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(filesToProcess, options, htmlFile =>
{
    string fileName = Path.GetFileName(htmlFile);
    string baseName = Path.GetFileNameWithoutExtension(htmlFile);
    string outputPath = Path.Combine(outputFolder, $"{baseName}.pdf");

    try
    {
        using var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
        pdf.SaveAs(outputPath);

        // Record success in checkpoint (thread-safe)
        lock (checkpointLock)
        {
            File.AppendAllText(checkpointPath, fileName + Environment.NewLine);
        }

        Interlocked.Increment(ref processed);
        Console.WriteLine($"[OK] {baseName}.pdf");
    }
    catch (Exception ex)
    {
        Interlocked.Increment(ref failed);

        // Log error for review
        lock (checkpointLock)
        {
            File.AppendAllText(errorLogPath,
                $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {fileName} | {ex.Message}{Environment.NewLine}");
        }

        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
    }
});

Console.WriteLine($"\nBatch complete:");
Console.WriteLine($"  Newly processed: {processed}");
Console.WriteLine($"  Failed: {failed}");
Console.WriteLine($"  Total completed: {completedFiles.Count + processed}");
Console.WriteLine($"  Checkpoint saved to: {checkpointPath}");
Imports IronPdf
Imports System
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Threading
Imports System.Collections.Generic

Module Program
    Sub Main()
        Dim inputFolder As String = "input/"
        Dim outputFolder As String = "output/"
        Dim checkpointPath As String = "checkpoint.txt"
        Dim errorLogPath As String = "errors.txt"

        Directory.CreateDirectory(outputFolder)

        ' Load checkpoint - files already processed successfully
        Dim completedFiles As New HashSet(Of String)()
        If File.Exists(checkpointPath) Then
            completedFiles = New HashSet(Of String)(File.ReadAllLines(checkpointPath))
            Console.WriteLine($"Resuming from checkpoint: {completedFiles.Count} files already processed")
        End If

        ' Get files to process (excluding already completed)
        Dim allFiles As String() = Directory.GetFiles(inputFolder, "*.html")
        Dim filesToProcess As String() = allFiles _
            .Where(Function(f) Not completedFiles.Contains(Path.GetFileName(f))) _
            .ToArray()

        Console.WriteLine($"Files to process: {filesToProcess.Length} (skipping {completedFiles.Count} already done)")

        Dim renderer As New ChromePdfRenderer()
        Dim checkpointLock As New Object()
        Dim processed As Integer = 0
        Dim failed As Integer = 0

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(filesToProcess, options, Sub(htmlFile)
                                                      Dim fileName As String = Path.GetFileName(htmlFile)
                                                      Dim baseName As String = Path.GetFileNameWithoutExtension(htmlFile)
                                                      Dim outputPath As String = Path.Combine(outputFolder, $"{baseName}.pdf")

                                                      Try
                                                          Using pdf = renderer.RenderHtmlFileAsPdf(htmlFile)
                                                              pdf.SaveAs(outputPath)
                                                          End Using

                                                          ' Record success in checkpoint (thread-safe)
                                                          SyncLock checkpointLock
                                                              File.AppendAllText(checkpointPath, fileName & Environment.NewLine)
                                                          End SyncLock

                                                          Interlocked.Increment(processed)
                                                          Console.WriteLine($"[OK] {baseName}.pdf")
                                                      Catch ex As Exception
                                                          Interlocked.Increment(failed)

                                                          ' Log error for review
                                                          SyncLock checkpointLock
                                                              File.AppendAllText(errorLogPath,
                                                                                  $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | {fileName} | {ex.Message}{Environment.NewLine}")
                                                          End SyncLock

                                                          Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                                      End Try
                                                  End Sub)

        Console.WriteLine(vbCrLf & "Batch complete:")
        Console.WriteLine($"  Newly processed: {processed}")
        Console.WriteLine($"  Failed: {failed}")
        Console.WriteLine($"  Total completed: {completedFiles.Count + processed}")
        Console.WriteLine($"  Checkpoint saved to: {checkpointPath}")
    End Sub
End Module
$vbLabelText   $csharpLabel

Kayıt dosyası, tamamlanan işlerin kalıcı bir kaydı işlevini görür. Boru hattı başlatıldığında, kontrol noktasını okuyarak daha önce başarıyla işlenmiş dosyaları geçer. Bir dosya işlemi tamamlandığında, yolu kontrol dosyasına eklenir. Bu yaklaşım basit, dosya tabanlı ve herhangi bir dış bağımlılığı gerektirmez.

Daha ileri düzey senaryolar için, kontrol noktası depoları olarak veritabanı tablosu veya dağıtılmış cache (Redis gibi) kullanmayı düşünün; bu dosyaları farklı makinelerde paralel olarak işleyen birçok çalışan varsa özellikle tavsiye edilir.


İşleme Öncesi ve Sonrası Doğrulama

Doğrulama, dayanıklı bir boru hattının vazgeçilmez parçasıdır. Ön işleme doğrulaması, işleme zamanı harcamadan sorunlu girdileri yakalar; işlem sonrası doğrulama, çıktının kalitesi ve uyumluluk gereksinimlerinizi karşıladığından emin olur.

Giriş

Bu uygulama, işleme döngüsünü hem PreValidate hem de PostValidate yardımcı işlevleriyle sarar. Ön doğrulama, işleme başlamadan önce dosya boyutu, içerik tipi ve temel HTML yapısını kontrol eder. Sonra doğrulaması, sonuç PDF'nin geçerli sayfa sayısı ve makul dosya boyutunu doğrular, doğrulanmış dosyaları ayrı bir klasöre taşırken başarısızlıkları manuel inceleme için red klasörüne yönlendirir.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-validation.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;

string inputFolder = "input/";
string outputFolder = "output/";
string validatedFolder = "validated/";
string rejectedFolder = "rejected/";

Directory.CreateDirectory(outputFolder);
Directory.CreateDirectory(validatedFolder);
Directory.CreateDirectory(rejectedFolder);

string[] inputFiles = Directory.GetFiles(inputFolder, "*.html");
var renderer = new ChromePdfRenderer();

int preValidationFailed = 0;
int processingFailed = 0;
int postValidationFailed = 0;
int succeeded = 0;

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount / 2
};

Parallel.ForEach(inputFiles, options, inputFile =>
{
    string fileName = Path.GetFileNameWithoutExtension(inputFile);
    string outputPath = Path.Combine(outputFolder, $"{fileName}.pdf");

    // Pre-validation: Check input file
    if (!PreValidate(inputFile))
    {
        Interlocked.Increment(ref preValidationFailed);
        Console.WriteLine($"[SKIP] {fileName}: Failed pre-validation");
        return;
    }

    try
    {
        // Process
        using var pdf = renderer.RenderHtmlFileAsPdf(inputFile);
        pdf.SaveAs(outputPath);

        // Post-validation: Check output file
        if (PostValidate(outputPath))
        {
            // Move to validated folder
            string validatedPath = Path.Combine(validatedFolder, $"{fileName}.pdf");
            File.Move(outputPath, validatedPath, overwrite: true);
            Interlocked.Increment(ref succeeded);
            Console.WriteLine($"[OK] {fileName}.pdf (validated)");
        }
        else
        {
            // Move to rejected folder for manual review
            string rejectedPath = Path.Combine(rejectedFolder, $"{fileName}.pdf");
            File.Move(outputPath, rejectedPath, overwrite: true);
            Interlocked.Increment(ref postValidationFailed);
            Console.WriteLine($"[REJECT] {fileName}.pdf: Failed post-validation");
        }
    }
    catch (Exception ex)
    {
        Interlocked.Increment(ref processingFailed);
        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
    }
});

Console.WriteLine($"\nValidation summary:");
Console.WriteLine($"  Succeeded: {succeeded}");
Console.WriteLine($"  Pre-validation failed: {preValidationFailed}");
Console.WriteLine($"  Processing failed: {processingFailed}");
Console.WriteLine($"  Post-validation failed: {postValidationFailed}");

// Pre-validation: Quick checks on input file
bool PreValidate(string filePath)
{
    try
    {
        var fileInfo = new FileInfo(filePath);

        // Check file exists and is readable
        if (!fileInfo.Exists) return false;

        // Check file is not empty
        if (fileInfo.Length == 0) return false;

        // Check file is not too large (e.g., 50MB limit)
        if (fileInfo.Length > 50 * 1024 * 1024) return false;

        // Quick content check - must be valid HTML
        string content = File.ReadAllText(filePath);
        if (string.IsNullOrWhiteSpace(content)) return false;
        if (!content.Contains("<html", StringComparison.OrdinalIgnoreCase) &&
            !content.Contains("<!DOCTYPE", StringComparison.OrdinalIgnoreCase))
        {
            return false;
        }

        return true;
    }
    catch
    {
        return false;
    }
}

// Post-validation: Verify output PDF meets requirements
bool PostValidate(string pdfPath)
{
    try
    {
        using var pdf = PdfDocument.FromFile(pdfPath);

        // Check PDF has at least one page
        if (pdf.PageCount < 1) return false;

        // Check file size is reasonable (not just header, not corrupted)
        var fileInfo = new FileInfo(pdfPath);
        if (fileInfo.Length < 1024) return false;

        return true;
    }
    catch
    {
        return false;
    }
}
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Threading
Imports System.Collections.Concurrent

Module Program
    Sub Main()
        Dim inputFolder As String = "input/"
        Dim outputFolder As String = "output/"
        Dim validatedFolder As String = "validated/"
        Dim rejectedFolder As String = "rejected/"

        Directory.CreateDirectory(outputFolder)
        Directory.CreateDirectory(validatedFolder)
        Directory.CreateDirectory(rejectedFolder)

        Dim inputFiles As String() = Directory.GetFiles(inputFolder, "*.html")
        Dim renderer As New ChromePdfRenderer()

        Dim preValidationFailed As Integer = 0
        Dim processingFailed As Integer = 0
        Dim postValidationFailed As Integer = 0
        Dim succeeded As Integer = 0

        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        }

        Parallel.ForEach(inputFiles, options, Sub(inputFile)
                                                  Dim fileName As String = Path.GetFileNameWithoutExtension(inputFile)
                                                  Dim outputPath As String = Path.Combine(outputFolder, $"{fileName}.pdf")

                                                  ' Pre-validation: Check input file
                                                  If Not PreValidate(inputFile) Then
                                                      Interlocked.Increment(preValidationFailed)
                                                      Console.WriteLine($"[SKIP] {fileName}: Failed pre-validation")
                                                      Return
                                                  End If

                                                  Try
                                                      ' Process
                                                      Using pdf = renderer.RenderHtmlFileAsPdf(inputFile)
                                                          pdf.SaveAs(outputPath)

                                                          ' Post-validation: Check output file
                                                          If PostValidate(outputPath) Then
                                                              ' Move to validated folder
                                                              Dim validatedPath As String = Path.Combine(validatedFolder, $"{fileName}.pdf")
                                                              File.Move(outputPath, validatedPath, overwrite:=True)
                                                              Interlocked.Increment(succeeded)
                                                              Console.WriteLine($"[OK] {fileName}.pdf (validated)")
                                                          Else
                                                              ' Move to rejected folder for manual review
                                                              Dim rejectedPath As String = Path.Combine(rejectedFolder, $"{fileName}.pdf")
                                                              File.Move(outputPath, rejectedPath, overwrite:=True)
                                                              Interlocked.Increment(postValidationFailed)
                                                              Console.WriteLine($"[REJECT] {fileName}.pdf: Failed post-validation")
                                                          End If
                                                      End Using
                                                  Catch ex As Exception
                                                      Interlocked.Increment(processingFailed)
                                                      Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                                  End Try
                                              End Sub)

        Console.WriteLine(vbCrLf & "Validation summary:")
        Console.WriteLine($"  Succeeded: {succeeded}")
        Console.WriteLine($"  Pre-validation failed: {preValidationFailed}")
        Console.WriteLine($"  Processing failed: {processingFailed}")
        Console.WriteLine($"  Post-validation failed: {postValidationFailed}")
    End Sub

    ' Pre-validation: Quick checks on input file
    Function PreValidate(filePath As String) As Boolean
        Try
            Dim fileInfo As New FileInfo(filePath)

            ' Check file exists and is readable
            If Not fileInfo.Exists Then Return False

            ' Check file is not empty
            If fileInfo.Length = 0 Then Return False

            ' Check file is not too large (e.g., 50MB limit)
            If fileInfo.Length > 50 * 1024 * 1024 Then Return False

            ' Quick content check - must be valid HTML
            Dim content As String = File.ReadAllText(filePath)
            If String.IsNullOrWhiteSpace(content) Then Return False
            If Not content.Contains("<html", StringComparison.OrdinalIgnoreCase) AndAlso
               Not content.Contains("<!DOCTYPE", StringComparison.OrdinalIgnoreCase) Then
                Return False
            End If

            Return True
        Catch
            Return False
        End Try
    End Function

    ' Post-validation: Verify output PDF meets requirements
    Function PostValidate(pdfPath As String) As Boolean
        Try
            Using pdf = PdfDocument.FromFile(pdfPath)
                ' Check PDF has at least one page
                If pdf.PageCount < 1 Then Return False

                ' Check file size is reasonable (not just header, not corrupted)
                Dim fileInfo As New FileInfo(pdfPath)
                If fileInfo.Length < 1024 Then Return False

                Return True
            End Using
        Catch
            Return False
        End Try
    End Function
End Module
$vbLabelText   $csharpLabel

Çıktı

Doğrulama girdi işleme ekran görüntüsü

Valide edilmiş bir dizin yapısını gösteren ve 5 dosya ile boş hata dizini

Tüm 5 dosya doğrulamayı geçti ve doğrulanmış klasöre taşındı.

Ön işleme doğrulaması hızlı olmalıdır — tamamen işlenmiş girdileri kontrol etmiyorsunuz, açıkça bozuk girdileri kontrol ediyorsunuz. İşlem sonrası doğrulama, özellikle çıktı belirli bir standardı (PDF/A, PDF/UA) geçmesi gereken uyumluluk dönüştürmeleri için daha kapsamlı olabilir. İşlem sonrasi doğrulamayı geçemeyen herhangi bir dosya, sessizce kabul edilmek yerine manuel inceleme için işaretlenmelidir.


Asenkron ve Paralel İşleme Kalıpları

IronPDF, hem Parallel.ForEach (thread tabanlı paralellik) hem de async/await (asenkron I/O) destekler. Her birini ne zaman kullanacağınızı anlamak — ve bunları etkili bir şekilde birleştirmek — verimliliği en üst düzeye çıkarmanın anahtarıdır.

Görev Paralel Kütüphane Entegrasyonu

Parallel.ForEach, CPU tabanlı toplu işlemler için en basit ve etkili yaklaşımdır. IronPDF'in render motoru CPU yoğundur (HTML ayrıştırma, CSS yerleşim, görüntü rasterizasyonu) ve Parallel.ForEach bu işi otomatik olarak tüm mevcut çekirdeklere dağıtır.

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-tpl.cs
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;

string inputFolder = "input/";
string outputFolder = "output/";

Directory.CreateDirectory(outputFolder);

string[] htmlFiles = Directory.GetFiles(inputFolder, "*.html");
var renderer = new ChromePdfRenderer();

Console.WriteLine($"Processing {htmlFiles.Length} files with {Environment.ProcessorCount} CPU cores");

int processed = 0;
var stopwatch = Stopwatch.StartNew();

// Configure parallelism based on system resources
// Rule of thumb: ProcessorCount / 2 for memory-intensive operations
var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2)
};

Console.WriteLine($"Max parallelism: {options.MaxDegreeOfParallelism}");

// Use Parallel.ForEach for CPU-bound batch operations
Parallel.ForEach(htmlFiles, options, htmlFile =>
{
    string fileName = Path.GetFileNameWithoutExtension(htmlFile);
    string outputPath = Path.Combine(outputFolder, $"{fileName}.pdf");

    try
    {
        // Render HTML to PDF
        using var pdf = renderer.RenderHtmlFileAsPdf(htmlFile);
        pdf.SaveAs(outputPath);

        int current = Interlocked.Increment(ref processed);

        // Progress reporting every 10 files
        if (current % 10 == 0)
        {
            double elapsed = stopwatch.Elapsed.TotalSeconds;
            double rate = current / elapsed;
            double remaining = (htmlFiles.Length - current) / rate;
            Console.WriteLine($"Progress: {current}/{htmlFiles.Length} ({rate:F1} files/sec, ~{remaining:F0}s remaining)");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"[ERROR] {fileName}: {ex.Message}");
    }
});

stopwatch.Stop();
double totalRate = processed / stopwatch.Elapsed.TotalSeconds;

Console.WriteLine($"\nComplete:");
Console.WriteLine($"  Files processed: {processed}/{htmlFiles.Length}");
Console.WriteLine($"  Total time: {stopwatch.Elapsed.TotalSeconds:F1}s");
Console.WriteLine($"  Average rate: {totalRate:F1} files/sec");
Console.WriteLine($"  Time per file: {stopwatch.Elapsed.TotalMilliseconds / processed:F0}ms");

// Memory monitoring helper (call between chunks for large batches)
void CheckMemoryPressure()
{
    const long memoryThreshold = 4L * 1024 * 1024 * 1024; // 4 GB
    long currentMemory = GC.GetTotalMemory(forceFullCollection: false);

    if (currentMemory > memoryThreshold)
    {
        Console.WriteLine($"Memory pressure detected ({currentMemory / 1024 / 1024}MB), forcing GC...");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }
}
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Threading
Imports System.Diagnostics

Module Program
    Sub Main()
        Dim inputFolder As String = "input/"
        Dim outputFolder As String = "output/"

        Directory.CreateDirectory(outputFolder)

        Dim htmlFiles As String() = Directory.GetFiles(inputFolder, "*.html")
        Dim renderer As New ChromePdfRenderer()

        Console.WriteLine($"Processing {htmlFiles.Length} files with {Environment.ProcessorCount} CPU cores")

        Dim processed As Integer = 0
        Dim stopwatch As Stopwatch = Stopwatch.StartNew()

        ' Configure parallelism based on system resources
        ' Rule of thumb: ProcessorCount / 2 for memory-intensive operations
        Dim options As New ParallelOptions With {
            .MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount \ 2)
        }

        Console.WriteLine($"Max parallelism: {options.MaxDegreeOfParallelism}")

        ' Use Parallel.ForEach for CPU-bound batch operations
        Parallel.ForEach(htmlFiles, options, Sub(htmlFile)
                                                 Dim fileName As String = Path.GetFileNameWithoutExtension(htmlFile)
                                                 Dim outputPath As String = Path.Combine(outputFolder, $"{fileName}.pdf")

                                                 Try
                                                     ' Render HTML to PDF
                                                     Using pdf = renderer.RenderHtmlFileAsPdf(htmlFile)
                                                         pdf.SaveAs(outputPath)
                                                     End Using

                                                     Dim current As Integer = Interlocked.Increment(processed)

                                                     ' Progress reporting every 10 files
                                                     If current Mod 10 = 0 Then
                                                         Dim elapsed As Double = stopwatch.Elapsed.TotalSeconds
                                                         Dim rate As Double = current / elapsed
                                                         Dim remaining As Double = (htmlFiles.Length - current) / rate
                                                         Console.WriteLine($"Progress: {current}/{htmlFiles.Length} ({rate:F1} files/sec, ~{remaining:F0}s remaining)")
                                                     End If
                                                 Catch ex As Exception
                                                     Console.WriteLine($"[ERROR] {fileName}: {ex.Message}")
                                                 End Try
                                             End Sub)

        stopwatch.Stop()
        Dim totalRate As Double = processed / stopwatch.Elapsed.TotalSeconds

        Console.WriteLine(vbCrLf & "Complete:")
        Console.WriteLine($"  Files processed: {processed}/{htmlFiles.Length}")
        Console.WriteLine($"  Total time: {stopwatch.Elapsed.TotalSeconds:F1}s")
        Console.WriteLine($"  Average rate: {totalRate:F1} files/sec")
        Console.WriteLine($"  Time per file: {stopwatch.Elapsed.TotalMilliseconds / processed:F0}ms")

        ' Memory monitoring helper (call between chunks for large batches)
        CheckMemoryPressure()
    End Sub

    Sub CheckMemoryPressure()
        Const memoryThreshold As Long = 4L * 1024 * 1024 * 1024 ' 4 GB
        Dim currentMemory As Long = GC.GetTotalMemory(forceFullCollection:=False)

        If currentMemory > memoryThreshold Then
            Console.WriteLine($"Memory pressure detected ({currentMemory \ 1024 \ 1024}MB), forcing GC...")
            GC.Collect()
            GC.WaitForPendingFinalizers()
            GC.Collect()
        End If
    End Sub
End Module
$vbLabelText   $csharpLabel

MaxDegreeOfParallelism seçeneği kritiktir. Bu olmadan, TPL tüm mevcut çekirdekleri kullanmayı dener, bu da her oluşturma işlemi kaynak yoğun olduğunda belleği aşırı yükleyebilir. Bunu sisteminizin mevcut RAM'ına bölünmüş olan tipik her render başına bellek tüketimini (genellikle karmaşık HTML için eşzamanlı render başına 100-300 MB) ayarlayın.

Eşzamanlılık Kontrolü (SemaphoreSlim)

Async I/O ile CPU tabanlı işleme karıştırırken Parallel.ForEach'den daha ince bir eş zamanlılık kontrolüne ihtiyaç duyduğunuzda, SemaphoreSlim aynı anda kaç işlem çalışacağını açıkça kontrol etmenizi sağlar. Desen basittir: istediğiniz eş zamanlılık limitiyle (örneğin, 4 eşzamanlı işleme) bir SemaphoreSlim oluşturun, her işleme öncesi WaitAsync çağırın ve sonra finally bloğunda Release yapın. Sonra tüm görevleri Task.WhenAll ile başlatın.

Bu kalıp, boru hattınız hem G/Ç tabanlı adımları (blob depolamadan dosya okuma, sonuçları bir veritabanına yazma) hem de CPU tabanlı adımları (PDF oluşturma) içerdiğinde özellikle yararlıdır. Semaphore, CPU tabanlı oluşturma eşzamanlılığını sınırlandırırken, G/Ç tabanlı adımların kısıtlama olmadan ilerlemesine izin verir.

Async/Await En İyi Uygulamalar

IronPDF, render yöntemlerinin async varyantlarını sağlar, bunlar arasında RenderHtmlAsPdfAsync, RenderUrlAsPdfAsync ve RenderHtmlFileAsPdfAsync bulunur. Bunlar, web uygulamaları için (istek iş parçacığını bloke etmenin kabul edilemez olduğu yerlerde) ve PDF oluşturmayı asenkron G/Ç işlemleriyle karıştıran boru hatları için idealdir.

Toplu işleme için birkaç önemli asenkron en iyi uygulama:

Senkrone IronPDF yöntemlerini Task.Run ile sarmalamayın — bunun yerine yerel async varyantlarını kullanın. Senkron yöntemleri Task.Run içinde sarmak, bir thread havuzu thread'ini boşa harcar ve fayda sağlamadan ek yük ekler.

Async görevlerde .Result veya .Wait() kullanmayın — bu, çağıran thread'i bloke eder ve UI veya ASP.NET bağlamlarında deadlock'a neden olabilir. Her zaman await kullanın.

Task.WhenAll çağrılarınızı toplu yapın — tüm görevleri bir kerede bekletmek yerine. 10.000 göreviniz varsa ve hepsine Task.WhenAll çağrısı yaparsanız aynı anda 10.000 eşzamanlı işlem başlatacaksınız. Bunun yerine, grup grup işleyip her grubu sıralı olarak beklemek için .Chunk(10) veya benzeri bir yaklaşım kullanın.

Bellek Tükenmesinin Önlenmesi

Toplu PDF işleme sırasında bellek tükenmesi en yaygın hata modudur. Defansif yaklaşım, her işleme öncesi GC.GetTotalMemory() ile bellek kullanımı izlemek ve tüketim bir eşik (örneğin, 4 GB veya mevcut RAM'in %80'i) aşarsa bir toplama tetiklemektir. Devam etmeden önce mümkün olduğunca bellek geri kazanmak için önce GC.Collect() ardından GC.WaitForPendingFinalizers() ve ikinci bir GC.Collect() çağrısı yapın. Bu, küçük bir duraklama ekler ancak dosya #30,000'de tüm toplu işleminizi çökerten bir OutOfMemoryException'nın yıkıcı alternatifini önler.

Bunu TPL bölümündeki MaxDegreeOfParallelism azaltımı ve bellek yönetimi bölümündeki using imha deseninden birleştirin ve bellek sorunlarına karşı üç katmanlı bir savunma elde edersiniz: eşzamanlılığı sınırlandırın, agresif bir şekilde kapatın ve güvenlik vanası ile izleyin.


Toplu İşler İçin Bulut Yayımlama

Modern toplu işleme giderek bulutta çalışıyor; buradaki iş yükü taleplerine göre işlem kaynaklarını ölçeklendirebilir ve sadece kullandığınız kadar ödeme yapabilirsiniz. IronPDF tüm önemli bulut platformlarında çalışır - her biri için toplu boru hatlarını nasıl tasarlayacağınız aşağıda.

Azure İşlevleri ile Dayanıklı İşlevler

Azure Dayanıklı İşlevler, fan-out/fan-in kalıpları için yerleşik koordinasyon sağlar ve bunlar toplu PDF işleme için doğal bir uyum sağlar. Orkestratör işlevi, dosyaların bir alt kümesini işleyen birden çok etkinlik işlev örneği arasında çalışmayı dağıtır. Yönlendiriciniz, her etkinlik işlevi bir ChromePdfRenderer örneği başlatır, dosyalarının dilimini işler ve yönlendirici sonuçları toplar, bir fan-out döngüsünde CallActivityAsync çağrıları yapar.

Azure İşlevleri için önemli hususlar: Varsayılan tüketim planı, işlev çağrısı başına 5 dakikalık bir zaman aşımına ve sınırlı belleğe sahiptir. Toplu işlem için, daha uzun zaman aşımı ve daha fazla belleği destekleyen Premium veya Ayrılmış planı kullanın. IronPDF, tam .NET çalışma zamanı gerektirir (kesilmemiş), bu nedenle .NET 8+ ile ilgili çalışma zamanı tanımlayıcısıyla işlev uygulamanızın yapılandırıldığından emin olun.

AWS Lambda ile İş Adımı İşlevleri

AWS İş Adımı İşlevleri, Azure Dayanıklı İşlevler'e benzer bir koordinasyon yeteneği sağlar. Durum makinesindeki her adım, bir dosya parçasını işleyen bir Lambda işlevini çağırır. Lambda işleyiciniz, bir S3 nesne anahtarları grubu alır, her PDF'yi PdfDocument.FromFile ile yükler, işleme hattınızı uygular (sıkıştırma, biçim dönüştürme vb.) ve sonuçları bir çıktı S3 deposuna yazar.

AWS Lambda'nın 15 dakikalık maksimum çalışma süresi ve sınırlı /tmp depolama alanı (varsayılan olarak 512 MB, isteğe bağlı olarak 10 GB'a kadar yapılandırılabilir) vardır. Büyük toplu işler için, iş yükünü parçalayıp her parçayı ayrı bir Lambda çağrısında işlemek için İş Adımı İşlevlerini kullanın. Ara sonuçları yerel depolama yerine S3'te saklayın.

Kubernetes İş Zamanlaması

Kendi Kubernetes kümelerini çalıştıran kuruluşlar için toplu PDF işleme, Kubernetes İşleri ve CronJobs ile iyi eşleşir. Her pod, bir kuyruktan dosyalar çeken (Azure Service Bus, RabbitMQ veya SQS), bunları IronPDF ile işleyen ve sonuçları nesne depolamaya yazan bir toplu iş çalı Mahallesi İşçi döngüsü, daha önceki bölümlerde ele alınan aynı deseni takip eder: bir mesaj kuyruğundan çıkarın, belgeyi işlemek için ChromePdfRenderer.RenderHtmlAsPdf() veya PdfDocument.FromFile() kullanın, sonucu yükleyin ve mesajı onaylayın. İşlemeyi try-catch içinde sarmalayarak direnç desenlerinden yeniden deneme mantığını kullanın ve pod başına eşzamanlılığı kontrol etmek için SemaphoreSlim kullanın.

IronPDF resmi Docker desteği sağlar ve Linux konteynerlerinde çalışır. Konteynerinizin işletim sistemi için uygun yerel çalışma zamanı paketleri ile IronPdf NuGet paketini kullanın (örneğin, Linux tabanlı görüntüler için IronPdf.Linux). Kubernetes için, IronPDF'nin bellek gereksinimlerine uygun kaynak isteklerini ve sınırlamalarını tanımlayın (tipik olarak eşzamanlılığa bağlı olarak pod başına 512 MB–2 GB). Yatay Pod Otomatik Ölçekleyici, kuyruk derinliğine bağlı olarak çalışanları ölçeklendirebilir ve kontrol noktası kalıbı, pod'lar tahliye edildiğinde hiçbir işin kaybolmamasını sağlar.

Job kuyruğu, hesaplama katmanı, nesne depolama ve izleme ‎paneliyle bulut-yerli toplu işleme hattını gösteren mimari diyagram


Maliyet Optimizasyon Stratejileri

Kaynak tahsisi konusunda dikkatli değilseniz bulut toplu işleme pahalı olabilir. En büyük etkiyi yaratan stratejiler şunlardır:

Hesabınızı doğru boyutlandırın. PDF oluşturma CPU ve bellek yoğundur, GPU yoğun değil. Genel amaçlı veya bellek odaklı yerine işlemci odaklı örnekleri (Azure'da C serisi, AWS'de C tipi) kullanın. Daha iyi fiyat-başına oluşturma oranları elde edersiniz.

Kesintiyi tolere edebilen toplu işler için spot/önceliksiz örnekler kullanın. Toplu PDF işleme, devam edilebilir olduğundan (kontrol noktası sayesinde), genellikle talep üzerine fiyatların %60-90 indirimli sunulması nedeniyle spot fiyatlandırma için ideal bir adaydır.

Çalışmayı yoğun olmayan saatlerde işleyin zaman çizelgeniz izin veriyorsa. Birçok bulut sağlayıcısı gece ve hafta sonlarında daha düşük fiyatlar veya daha fazla seçenek sunar.

Erken sıkıştır, bir kez depola. İşleme hattınızın bir parçası olarak sıkıştırma çalıştırın, ayrı bir adım olarak değil. Başlangıçtan itibaren sıkıştırılmış PDF'lerin depolanması, arşivin ömrü boyunca sürekli depolama maliyetlerini azaltır.

Depolamanızı aşamalandırın. Sık erişilen işlemi tamamlanmış PDF'ler sıcak depolamada saklanmalıdır; nadiren erişilen arşivlenmiş PDF'ler soğuk veya arşiv katmanlarına taşınmalıdır (Azure Cool/Archive, AWS S3 Glacier). Bu yalnızca depolama maliyetlerini %50-80 oranında azaltabilir.


Gerçek Dünya Boru Hattı Örneği

Tüm iş akışını gösteren eksiksiz, üretim dereceli bir toplu iş boru hattıyla her şeyi birleştirelim: Alma → Doğrulama → İşleme → Arşivleme → Raporlama.

Bu örnek, bir HTML fatura şablonları dizisini işler, bunları PDF olarak oluşturur, çıktıyı sıkıştırır, arşiv uyumluluğu için PDF/A-3b'ye dönüştürür, sonucu doğrular ve sonunda bir özet raporu üretir.

Yukarıdaki toplu dönüştürme örneğinden aynı 5 HTML faturayı kullanarak...

:path=/static-assets/pdf/content-code-examples/tutorials/batch-pdf-processing-csharp/batch-processing-full-pipeline.cs
using IronPdf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.Json;

// Configuration
var config = new PipelineConfig
{
    InputFolder = "input/",
    OutputFolder = "output/",
    ArchiveFolder = "archive/",
    ErrorFolder = "errors/",
    CheckpointPath = "pipeline-checkpoint.json",
    ReportPath = "pipeline-report.json",
    MaxConcurrency = Math.Max(1, Environment.ProcessorCount / 2),
    MaxRetries = 3,
    JpegQuality = 70
};

// Initialize folders
Directory.CreateDirectory(config.OutputFolder);
Directory.CreateDirectory(config.ArchiveFolder);
Directory.CreateDirectory(config.ErrorFolder);

// Load checkpoint for resume capability
var checkpoint = LoadCheckpoint(config.CheckpointPath);
var results = new ConcurrentBag<ProcessingResult>();
var stopwatch = Stopwatch.StartNew();

// Get files to process
string[] allFiles = Directory.GetFiles(config.InputFolder, "*.html");
string[] filesToProcess = allFiles
    .Where(f => !checkpoint.CompletedFiles.Contains(Path.GetFileName(f)))
    .ToArray();

Console.WriteLine($"Pipeline starting:");
Console.WriteLine($"  Total files: {allFiles.Length}");
Console.WriteLine($"  Already processed: {checkpoint.CompletedFiles.Count}");
Console.WriteLine($"  To process: {filesToProcess.Length}");
Console.WriteLine($"  Concurrency: {config.MaxConcurrency}");

var renderer = new ChromePdfRenderer();
var checkpointLock = new object();

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = config.MaxConcurrency
};

Parallel.ForEach(filesToProcess, options, inputFile =>
{
    var result = new ProcessingResult
    {
        FileName = Path.GetFileName(inputFile),
        StartTime = DateTime.UtcNow
    };

    try
    {
        // Stage: Pre-validation
        if (!ValidateInput(inputFile))
        {
            result.Status = "PreValidationFailed";
            result.Error = "Input file failed validation";
            results.Add(result);
            return;
        }

        string baseName = Path.GetFileNameWithoutExtension(inputFile);
        string tempPath = Path.Combine(config.OutputFolder, $"{baseName}.pdf");
        string archivePath = Path.Combine(config.ArchiveFolder, $"{baseName}.pdf");

        // Stage: Process with retry
        PdfDocument pdf = null;
        int attempt = 0;
        bool success = false;

        while (attempt < config.MaxRetries && !success)
        {
            attempt++;
            try
            {
                pdf = renderer.RenderHtmlFileAsPdf(inputFile);
                success = true;
            }
            catch (Exception ex) when (IsTransient(ex) && attempt < config.MaxRetries)
            {
                Thread.Sleep((int)Math.Pow(2, attempt) * 500);
            }
        }

        if (!success || pdf == null)
        {
            result.Status = "ProcessingFailed";
            result.Error = "Max retries exceeded";
            results.Add(result);
            return;
        }

        using (pdf)
        {
            // Stage: Compress and convert to PDF/A-3b for archival
            pdf.SaveAsPdfA(tempPath, PdfAVersions.PdfA3b);
        }

        // Stage: Post-validation
        if (!ValidateOutput(tempPath))
        {
            File.Move(tempPath, Path.Combine(config.ErrorFolder, $"{baseName}.pdf"), overwrite: true);
            result.Status = "PostValidationFailed";
            result.Error = "Output file failed validation";
            results.Add(result);
            return;
        }

        // Stage: Archive
        File.Move(tempPath, archivePath, overwrite: true);

        // Update checkpoint
        lock (checkpointLock)
        {
            checkpoint.CompletedFiles.Add(result.FileName);
            SaveCheckpoint(config.CheckpointPath, checkpoint);
        }

        result.Status = "Success";
        result.OutputSize = new FileInfo(archivePath).Length;
        result.EndTime = DateTime.UtcNow;
        results.Add(result);

        Console.WriteLine($"[OK] {baseName}.pdf ({result.OutputSize / 1024}KB)");
    }
    catch (Exception ex)
    {
        result.Status = "Error";
        result.Error = ex.Message;
        result.EndTime = DateTime.UtcNow;
        results.Add(result);
        Console.WriteLine($"[ERROR] {result.FileName}: {ex.Message}");
    }
});

stopwatch.Stop();

// Generate report
var report = new PipelineReport
{
    TotalFiles = allFiles.Length,
    ProcessedThisRun = results.Count,
    Succeeded = results.Count(r => r.Status == "Success"),
    PreValidationFailed = results.Count(r => r.Status == "PreValidationFailed"),
    ProcessingFailed = results.Count(r => r.Status == "ProcessingFailed"),
    PostValidationFailed = results.Count(r => r.Status == "PostValidationFailed"),
    Errors = results.Count(r => r.Status == "Error"),
    TotalDuration = stopwatch.Elapsed,
    AverageFileTime = results.Any() ? TimeSpan.FromMilliseconds(stopwatch.Elapsed.TotalMilliseconds / results.Count) : TimeSpan.Zero
};

string reportJson = JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(config.ReportPath, reportJson);

Console.WriteLine($"\n=== Pipeline Complete ===");
Console.WriteLine($"Succeeded: {report.Succeeded}");
Console.WriteLine($"Failed: {report.PreValidationFailed + report.ProcessingFailed + report.PostValidationFailed + report.Errors}");
Console.WriteLine($"Duration: {report.TotalDuration.TotalMinutes:F1} minutes");
Console.WriteLine($"Report: {config.ReportPath}");

// Helper methods
bool ValidateInput(string path)
{
    try
    {
        var info = new FileInfo(path);
        if (!info.Exists || info.Length == 0 || info.Length > 50 * 1024 * 1024) return false;
        string content = File.ReadAllText(path);
        return content.Contains("<html", StringComparison.OrdinalIgnoreCase) ||
               content.Contains("<!DOCTYPE", StringComparison.OrdinalIgnoreCase);
    }
    catch { return false; }
}

bool ValidateOutput(string path)
{
    try
    {
        using var pdf = PdfDocument.FromFile(path);
        return pdf.PageCount > 0 && new FileInfo(path).Length > 1024;
    }
    catch { return false; }
}

bool IsTransient(Exception ex) =>
    ex is IOException || ex is OutOfMemoryException ||
    ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase);

Checkpoint LoadCheckpoint(string path)
{
    if (File.Exists(path))
    {
        string json = File.ReadAllText(path);
        return JsonSerializer.Deserialize<Checkpoint>(json) ?? new Checkpoint();
    }
    return new Checkpoint();
}

void SaveCheckpoint(string path, Checkpoint cp) =>
    File.WriteAllText(path, JsonSerializer.Serialize(cp));


ata classes
s PipelineConfig

public string InputFolder { get; set; } = "";
public string OutputFolder { get; set; } = "";
public string ArchiveFolder { get; set; } = "";
public string ErrorFolder { get; set; } = "";
public string CheckpointPath { get; set; } = "";
public string ReportPath { get; set; } = "";
public int MaxConcurrency { get; set; }
public int MaxRetries { get; set; }
public int JpegQuality { get; set; }


s Checkpoint

public HashSet<string> CompletedFiles { get; set; } = new();


s ProcessingResult

public string FileName { get; set; } = "";
public string Status { get; set; } = "";
public string Error { get; set; } = "";
public long OutputSize { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }


s PipelineReport

public int TotalFiles { get; set; }
public int ProcessedThisRun { get; set; }
public int Succeeded { get; set; }
public int PreValidationFailed { get; set; }
public int ProcessingFailed { get; set; }
public int PostValidationFailed { get; set; }
public int Errors { get; set; }
public TimeSpan TotalDuration { get; set; }
public TimeSpan AverageFileTime { get; set; }
Imports IronPdf
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Threading
Imports System.Collections.Concurrent
Imports System.Diagnostics
Imports System.Text.Json

' Configuration
Dim config As New PipelineConfig With {
    .InputFolder = "input/",
    .OutputFolder = "output/",
    .ArchiveFolder = "archive/",
    .ErrorFolder = "errors/",
    .CheckpointPath = "pipeline-checkpoint.json",
    .ReportPath = "pipeline-report.json",
    .MaxConcurrency = Math.Max(1, Environment.ProcessorCount \ 2),
    .MaxRetries = 3,
    .JpegQuality = 70
}

' Initialize folders
Directory.CreateDirectory(config.OutputFolder)
Directory.CreateDirectory(config.ArchiveFolder)
Directory.CreateDirectory(config.ErrorFolder)

' Load checkpoint for resume capability
Dim checkpoint As Checkpoint = LoadCheckpoint(config.CheckpointPath)
Dim results As New ConcurrentBag(Of ProcessingResult)()
Dim stopwatch As Stopwatch = Stopwatch.StartNew()

' Get files to process
Dim allFiles As String() = Directory.GetFiles(config.InputFolder, "*.html")
Dim filesToProcess As String() = allFiles.
    Where(Function(f) Not checkpoint.CompletedFiles.Contains(Path.GetFileName(f))).
    ToArray()

Console.WriteLine("Pipeline starting:")
Console.WriteLine($"  Total files: {allFiles.Length}")
Console.WriteLine($"  Already processed: {checkpoint.CompletedFiles.Count}")
Console.WriteLine($"  To process: {filesToProcess.Length}")
Console.WriteLine($"  Concurrency: {config.MaxConcurrency}")

Dim renderer As New ChromePdfRenderer()
Dim checkpointLock As New Object()

Dim options As New ParallelOptions With {
    .MaxDegreeOfParallelism = config.MaxConcurrency
}

Parallel.ForEach(filesToProcess, options, Sub(inputFile)
    Dim result As New ProcessingResult With {
        .FileName = Path.GetFileName(inputFile),
        .StartTime = DateTime.UtcNow
    }

    Try
        ' Stage: Pre-validation
        If Not ValidateInput(inputFile) Then
            result.Status = "PreValidationFailed"
            result.Error = "Input file failed validation"
            results.Add(result)
            Return
        End If

        Dim baseName As String = Path.GetFileNameWithoutExtension(inputFile)
        Dim tempPath As String = Path.Combine(config.OutputFolder, $"{baseName}.pdf")
        Dim archivePath As String = Path.Combine(config.ArchiveFolder, $"{baseName}.pdf")

        ' Stage: Process with retry
        Dim pdf As PdfDocument = Nothing
        Dim attempt As Integer = 0
        Dim success As Boolean = False

        While attempt < config.MaxRetries AndAlso Not success
            attempt += 1
            Try
                pdf = renderer.RenderHtmlFileAsPdf(inputFile)
                success = True
            Catch ex As Exception When IsTransient(ex) AndAlso attempt < config.MaxRetries
                Thread.Sleep(CInt(Math.Pow(2, attempt)) * 500)
            End Try
        End While

        If Not success OrElse pdf Is Nothing Then
            result.Status = "ProcessingFailed"
            result.Error = "Max retries exceeded"
            results.Add(result)
            Return
        End If

        Using pdf
            ' Stage: Compress and convert to PDF/A-3b for archival
            pdf.SaveAsPdfA(tempPath, PdfAVersions.PdfA3b)
        End Using

        ' Stage: Post-validation
        If Not ValidateOutput(tempPath) Then
            File.Move(tempPath, Path.Combine(config.ErrorFolder, $"{baseName}.pdf"), overwrite:=True)
            result.Status = "PostValidationFailed"
            result.Error = "Output file failed validation"
            results.Add(result)
            Return
        End If

        ' Stage: Archive
        File.Move(tempPath, archivePath, overwrite:=True)

        ' Update checkpoint
        SyncLock checkpointLock
            checkpoint.CompletedFiles.Add(result.FileName)
            SaveCheckpoint(config.CheckpointPath, checkpoint)
        End SyncLock

        result.Status = "Success"
        result.OutputSize = New FileInfo(archivePath).Length
        result.EndTime = DateTime.UtcNow
        results.Add(result)

        Console.WriteLine($"[OK] {baseName}.pdf ({result.OutputSize \ 1024}KB)")
    Catch ex As Exception
        result.Status = "Error"
        result.Error = ex.Message
        result.EndTime = DateTime.UtcNow
        results.Add(result)
        Console.WriteLine($"[ERROR] {result.FileName}: {ex.Message}")
    End Try
End Sub)

stopwatch.Stop()

' Generate report
Dim report As New PipelineReport With {
    .TotalFiles = allFiles.Length,
    .ProcessedThisRun = results.Count,
    .Succeeded = results.Count(Function(r) r.Status = "Success"),
    .PreValidationFailed = results.Count(Function(r) r.Status = "PreValidationFailed"),
    .ProcessingFailed = results.Count(Function(r) r.Status = "ProcessingFailed"),
    .PostValidationFailed = results.Count(Function(r) r.Status = "PostValidationFailed"),
    .Errors = results.Count(Function(r) r.Status = "Error"),
    .TotalDuration = stopwatch.Elapsed,
    .AverageFileTime = If(results.Any(), TimeSpan.FromMilliseconds(stopwatch.Elapsed.TotalMilliseconds / results.Count), TimeSpan.Zero)
}

Dim reportJson As String = JsonSerializer.Serialize(report, New JsonSerializerOptions With {.WriteIndented = True})
File.WriteAllText(config.ReportPath, reportJson)

Console.WriteLine(vbCrLf & "=== Pipeline Complete ===")
Console.WriteLine($"Succeeded: {report.Succeeded}")
Console.WriteLine($"Failed: {report.PreValidationFailed + report.ProcessingFailed + report.PostValidationFailed + report.Errors}")
Console.WriteLine($"Duration: {report.TotalDuration.TotalMinutes:F1} minutes")
Console.WriteLine($"Report: {config.ReportPath}")

' Helper methods
Function ValidateInput(path As String) As Boolean
    Try
        Dim info As New FileInfo(path)
        If Not info.Exists OrElse info.Length = 0 OrElse info.Length > 50 * 1024 * 1024 Then Return False
        Dim content As String = File.ReadAllText(path)
        Return content.Contains("<html", StringComparison.OrdinalIgnoreCase) OrElse
               content.Contains("<!DOCTYPE", StringComparison.OrdinalIgnoreCase)
    Catch
        Return False
    End Try
End Function

Function ValidateOutput(path As String) As Boolean
    Try
        Using pdf = PdfDocument.FromFile(path)
            Return pdf.PageCount > 0 AndAlso New FileInfo(path).Length > 1024
        End Using
    Catch
        Return False
    End Try
End Function

Function IsTransient(ex As Exception) As Boolean
    Return TypeOf ex Is IOException OrElse TypeOf ex Is OutOfMemoryException OrElse
           ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase)
End Function

Function LoadCheckpoint(path As String) As Checkpoint
    If File.Exists(path) Then
        Dim json As String = File.ReadAllText(path)
        Return JsonSerializer.Deserialize(Of Checkpoint)(json) OrElse New Checkpoint()
    End If
    Return New Checkpoint()
End Function

Sub SaveCheckpoint(path As String, cp As Checkpoint)
    File.WriteAllText(path, JsonSerializer.Serialize(cp))
End Sub

' Data classes
Class PipelineConfig
    Public Property InputFolder As String = ""
    Public Property OutputFolder As String = ""
    Public Property ArchiveFolder As String = ""
    Public Property ErrorFolder As String = ""
    Public Property CheckpointPath As String = ""
    Public Property ReportPath As String = ""
    Public Property MaxConcurrency As Integer
    Public Property MaxRetries As Integer
    Public Property JpegQuality As Integer
End Class

Class Checkpoint
    Public Property CompletedFiles As HashSet(Of String) = New HashSet(Of String)()
End Class

Class ProcessingResult
    Public Property FileName As String = ""
    Public Property Status As String = ""
    Public Property Error As String = ""
    Public Property OutputSize As Long
    Public Property StartTime As DateTime
    Public Property EndTime As DateTime
End Class

Class PipelineReport
    Public Property TotalFiles As Integer
    Public Property ProcessedThisRun As Integer
    Public Property Succeeded As Integer
    Public Property PreValidationFailed As Integer
    Public Property ProcessingFailed As Integer
    Public Property PostValidationFailed As Integer
    Public Property Errors As Integer
    Public Property TotalDuration As TimeSpan
    Public Property AverageFileTime As TimeSpan
End Class
$vbLabelText   $csharpLabel

Çıktı

Başarılı sayım, başarısız sayım ve toplam süre ile toplu işlem sonuçlarını gösteren boru hattı raporu JSON

Toplu işleme sonuçlarını gösteren boru hattı raporu.

Bu boru hattı, bu öğreticide ele alınan her kalıbı içerir: kontrollü eşzamanlılıkla paralel işleme, başarısızlıkta atlama ile dosya başına hata yönetimi, geçici hatalar için yeniden deneme mantığı, çökmeden sonra devam etme için kontrol noktası oluşturma, öncesi ve sonrası doğrulama, açık bir şekilde atmayla bellek yönetimi ve nihai bir özet raporu ile kapsamlı kayır kaydı.

Bu boru hattının çıktısı sıkıştırılmış, PDF/A-3b uyumlu arşiv dosyaları dizini, devam edebilme kapasitesi için bir kontrol noktası dosyası, işlenemeyen dosyalar için bir hata kaydı ve işleme istatistikleriyle bir özet raporu içerir. Herhangi bir ciddi toplu PDF işleme yükü için istediğiniz kalıp budur.


Sonraki Adımlar

Ölçekli toplu PDF işleme, sadece bir döngü içinde bir oluşturma yöntemini çağırmakla ilgili değildir. Eşzamanlılık, bellek yönetimi, hata yönetimi ve dağıtım etrafında düşünceli bir mimari gerektirir — ve bunu çalıştırmak için doğru kütüphane gerekir. IronPDF, iş parçacığı güvenli oluşturma motoru, asenkron API yüzeyi, sıkıştırma araçları ve biçim dönüşüm yetenekleri sağlar, bu da herhangi bir .NET toplu PDF hattının temelini oluşturur.

Birçok belge üreticisine bir gecede binlerce PDF üreten bir gece rapor üreticisinden, eski bir belge arşivini PDF/A uyumluluğuna taşımaktan veya Kubernetes üzerinde bulut yerlisi bir işleme hizmeti oluşturmaktan bağımsız olarak, bu öğretici kalıpları size inşa edilecek kanıtlanmış bir çerçeve sağlıyor. Kontrollü eşzamanlılıkla paralel işleme, verimliliği yüksek tutar. Başarısızlıkta atlama ve yeniden deneme mantığı, bireysel dosyalar sorun çıkardığında hattı devam ettirir. Çekim noktası asla ilerlemeyi kaybetmemenizi sağlar. Ve bulut dağıtım kalıpları, iş yükünüze uygun şekilde ölçeklemenize izin verir.

Yapmaya hazır mısınız? IronPDF'yi İndirin ve ücretsiz deneme sürümüyle deneyin — aynı kitaplık, tek dosya oluşturma işlemlerinden yüz bin dosyalık toplu işlemlere kadar her şeyi yönetir. Özel kullanım durumunuza yönelik ölçekleme, dağıtım veya mimari hakkında sorularınız varsa, mühendislik destek ekibiyle iletişime geçin — ekiplerin her ölçekteki toplu iş hatlarını kurmasına yardımcı olduk ve size doğru kurulumda yardımcı olmaktan mutluluk duyarız.

Sıkça Sorulan Sorular

C# ile toplu PDF işleme nedir?

C#'da toplu PDF işleme, aynı anda birçok PDF belgesinin otomatik olarak yönetilmesi anlamına gelir ve bu yöntem, belge iş akışlarını ölçekli olarak otomatikleştirmek için idealdir.

IronPDF toplu PDF işlemeye nasıl yardımcı olabilir?

IronPDF, C#'da toplu PDF işlemeyi kolaylaştıran güçlü araçlar ve kütüphaneler sağlar. Paralel işleme desteği sunar, böylece binlerce PDF'yi aynı anda verimli bir şekilde yönetebilirsiniz.

IronPDF ile paralel işlemeyi kullanmanın faydaları nelerdir?

IronPDF ile paralel işleme, PDF'lerin daha hızlı ve verimli bir şekilde toplu işlenmesini sağlar. Bu yaklaşım, kaynak kullanımını en üst düzeye çıkarır ve işlem süresini önemli ölçüde azaltır.

IronPDF bulut platformlarında toplu işlem için dağıtılabilir mi?

Evet, IronPDF, Azure Functions, AWS Lambda ve Kubernetes gibi bulut platformlarında dağıtılabilir, esnek ve ölçeklenebilir toplu PDF işleme sağlayarak.

IronPDF, toplu PDF işlem sırasında hataları nasıl yönetir?

IronPDF, toplu PDF işleme sırasında güvenilirliği sağlamak için hata yönetimi ve yeniden deneme mantığı özellikleri içerir. Bu özellikler, manuel müdahale olmadan hataları yönetmeye ve düzeltmeye yardımcı olur.

IronPDF ile PDF işlemde yeniden deneme mantığının rolü nedir?

IronPDF'deki yeniden deneme mantığı, geçici sorunların toplu işleme iş akışını kesintiye uğratmamasını sağlar. Bir hata meydana gelirse, IronPDF başarısız olan belgeyi otomatik olarak yeniden işleme girişiminde bulunabilir.

C# toplu PDF işleme için neden uygun bir dildir?

C#, kapsamlı kütüphaneleri ve çerçeveleri ile güçlüdür, bu da onu toplu PDF işleme için ideal kılar. Verimli belge otomasyonu için IronPDF ile sorunsuz bir şekilde entegre olur.

IronPDF, PDF belgelerinin güvenliğini işleme sırasında nasıl sağlar?

IronPDF, işlenen belgelerin gizli ve güvenli kalmasını sağlamak için şifreleme ve parola koruma özellikleri sağlayarak PDF belgelerinin güvenli bir şekilde yönetilmesini destekler.

İşletmelerde toplu PDF işlemenin bazı kullanım durumları nelerdir?

İşletmeler, toplu fatura oluşturma, belge dijitalleştirme ve büyük ölçekli rapor dağıtımı gibi görevler için toplu PDF işlemeden faydalanır. IronPDF, bu kullanım durumlarını belge iş akışlarını otomatikleştirip basitleştirerek kolaylaştırır.

IronPDF farklı PDF formatlarını ve sürümlerini işleyebilir mi?

Evet, IronPDF, çeşitli PDF formatlarını ve sürümlerini işlermek üzere tasarlanmıştır, böylece toplu işleme görevlerinde uyumluluk ve esneklik sağlar.

Ahmad Sohail
Full Stack Geliştirici

Ahmad, C#, Python ve web teknolojilerinde güçlü bir temele sahip bir full-stack geliştiricidir. Ölçeklenebilir yazılım çözümleri oluşturma konusunda derin bir ilgiye sahiptir ve tasarım ile işlevselliğin gerçek dünya uygulamalarında nasıl birleştiğini keşfetmekten ...

Daha Fazlasını Oku
Başlamaya Hazır mısınız?
Nuget Indirmeler 18,332,619 | Sürüm: 2026.4 yeni yayınlandı
Still Scrolling Icon

Hala Kaydiriyor musunuz?

Hızlı bir kanit mi istiyorsunuz? PM > Install-Package IronPdf
bir örnek çalıştır HTML'nizin PDF olduğunu izleyin.