Pico Alto de Memoria al Leer PDF en Archivos PDF Grandes
Llamar a BarcodeReader.ReadPdf() una vez para todo un PDF grande renderiza cada página solicitada para escanear a la DPI configurada, por lo que la memoria máxima escala con el conteo de páginas y la resolución en lugar de mantenerse constante. En un archivo de 248 páginas y 80 MB a 300 DPI, la memoria osciló entre 3GB y 8GB con picos de hasta 12GB, suficiente para fallar en entornos con limitaciones de memoria.
BarcodeReader.ReadPdf() procesa cada página entregada en una sola operación. Cuando se lee al completo el rango de páginas de un documento grande, alta DPI, todas esas páginas se mantienen juntas en memoria, de modo que el uso máximo crece con el tamaño del conjunto de páginas. Esto refleja cómo se agrupan las lecturas, no un defecto confirmado en IronBarcode.
Solución
1. Leer en fragmentos de páginas fijos
Divide el documento en fragmentos de páginas fijas con PdfBarcodeReaderOptions.PageNumbers para que solo un fragmento esté en memoria a la vez. Un chunkSize más pequeño reduce la memoria máxima pero agrega más pasadas, así que ajústalo al límite de memoria del entorno.
var barcodeMetadata = new List<BarcodeResult>();
var pageChunks = Enumerable.Range(1, pdf.PageCount)
.GroupBy(i => i / 50)
.Select(g => g.ToArray())
.ToList();
foreach (var chunk in pageChunks)
{
var readerOptions = new PdfBarcodeReaderOptions
{
ExpectMultipleBarcodes = true,
ExpectBarcodeTypes = IronBarCode.BarcodeEncoding.AllOneDimensional,
Speed = ReadingSpeed.Detailed,
ScanMode = BarcodeScanMode.OnlyDetectionModel,
DPI = 300,
PageNumbers = chunk
};
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}");
var chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions);
if (chunkResult != null && chunkResult.Count > 0)
{
barcodeMetadata.AddRange(chunkResult);
}
// Optional memory pressure relief
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}");
var barcodeMetadata = new List<BarcodeResult>();
var pageChunks = Enumerable.Range(1, pdf.PageCount)
.GroupBy(i => i / 50)
.Select(g => g.ToArray())
.ToList();
foreach (var chunk in pageChunks)
{
var readerOptions = new PdfBarcodeReaderOptions
{
ExpectMultipleBarcodes = true,
ExpectBarcodeTypes = IronBarCode.BarcodeEncoding.AllOneDimensional,
Speed = ReadingSpeed.Detailed,
ScanMode = BarcodeScanMode.OnlyDetectionModel,
DPI = 300,
PageNumbers = chunk
};
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}");
var chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions);
if (chunkResult != null && chunkResult.Count > 0)
{
barcodeMetadata.AddRange(chunkResult);
}
// Optional memory pressure relief
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}");
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports IronBarCode
Dim barcodeMetadata As New List(Of BarcodeResult)()
Dim pageChunks = Enumerable.Range(1, pdf.PageCount) _
.GroupBy(Function(i) i \ 50) _
.Select(Function(g) g.ToArray()) _
.ToList()
For Each chunk In pageChunks
Dim readerOptions As New PdfBarcodeReaderOptions With {
.ExpectMultipleBarcodes = True,
.ExpectBarcodeTypes = BarcodeEncoding.AllOneDimensional,
.Speed = ReadingSpeed.Detailed,
.ScanMode = BarcodeScanMode.OnlyDetectionModel,
.DPI = 300,
.PageNumbers = chunk
}
Console.WriteLine($"Processing pages: Pages {chunk.First()}-{chunk.Last()}")
Dim chunkResult = BarcodeReader.ReadPdf(pdf.Stream, readerOptions)
If chunkResult IsNot Nothing AndAlso chunkResult.Count > 0 Then
barcodeMetadata.AddRange(chunkResult)
End If
' Optional memory pressure relief
GC.Collect()
GC.WaitForPendingFinalizers()
Next
Console.WriteLine($"Total barcodes found: {barcodeMetadata.Count}")
Cada iteración solo lee las páginas nombradas en PageNumbers, por lo que la memoria disminuye entre fragmentos en lugar de acumularse en todo el archivo.
2. Caché del flujo PDF y restablece su posición
Lee el flujo en una variable local antes del bucle, luego rebobínalo por fragmento. La propiedad Stream puede asignar memoria en cada acceso, así que almacénala en var pdfStream = pdf.Stream; y llama a pdfStream.Seek(0, SeekOrigin.Begin) antes de cada lectura de fragmento.
3. Construir las opciones del lector una vez
Construye PdfBarcodeReaderOptions fuera del bucle y cambia solo PageNumbers por fragmento, en lugar de asignar un nuevo objeto de opciones en cada iteración.
4. Pre-tamaño de la lista de resultados
Inicializa la lista de resultados con una capacidad estimada para evitar duplicar internamente una matriz repetidamente a medida que se añaden resultados. La repro estima aproximadamente un código de barras por cada diez páginas como cifra inicial.

