{"Translation":"Construir API de esc\u00e1ner de c\u00f3digos de barras en C#"}
Cree una API de escáner de código de barras RESTful usando IronBarcode y ASP.NET Core para administrar códigos de barras de imágenes, PDF y escaneos dañados sin hardware, lo que garantiza un rendimiento confiable en Windows, Linux y macOS.
IronBarcode permite el escaneo de códigos de barras profesional a través de API RESTful en ASP.NET Core, procesando imágenes, PDF y escaneos dañados sin dependencias de hardware y al mismo tiempo admitiendo la implementación multiplataforma con funciones de seguridad y cumplimiento integradas.
¿Qué es el desafío del código de barras empresarial?
Imagínese ser el arquitecto empresarial de una empresa de logística de Fortune 500. Sus equipos de almacén luchan con etiquetas de envío dañadas, su departamento de finanzas necesita procesar miles de PDF facturados con códigos de barras integrados y su equipo de seguridad de TI exige una solución que cumpla con la normativa SOC2 sin introducir dependencias de hardware en su infraestructura global.
Este escenario es común para los equipos empresariales que buscan descubrir las capacidades de la API REST de IronBarcode . Lo que comenzó como un simple requisito para leer códigos de barras evolucionó hasta convertirse en una solución de escaneo completa que ahora procesa más de 2 millones de códigos de barras mensualmente en toda su organización.
Tradicionalmente, esto habría requerido costosos escáneres de hardware en cada ubicación, una gestión compleja de controladores y revisiones de seguridad importantes. En lugar de ello, crearon una API RESTful centralizada a la que cualquier departamento podía acceder de forma segura, ya sea para escanear etiquetas de almacén dañadas o extraer datos de facturas de archivos PDF de proveedores.
Este tutorial lo guiará en la creación de esa misma API de escáner de código de barras profesional. Aprenderá cómo crear puntos finales seguros que aborden desafíos del mundo real, como etiquetas rotas, imágenes torcidas y documentos de varias páginas. Lo más importante es que verá cómo implementar esto con la seguridad, escalabilidad y estabilidad del proveedor que exigen los entornos empresariales.
¿Cómo crear una API de escáner de C# confiable?
La creación de una API de escáner de código de barras empresarial requiere abordar varias cuestiones críticas de antemano. Los equipos de seguridad necesitan garantías sobre el manejo de datos. Los responsables de cumplimiento necesitan registros de auditoría. Los equipos de operaciones exigen confiabilidad en diferentes plataformas. La documentación completa de IronBarcode aborda cada una de estas preocupaciones sistemáticamente.
La belleza de un enfoque RESTful radica en su simplicidad y seguridad. Al centralizar el procesamiento de códigos de barras detrás de puntos finales de API seguros, usted mantiene un control total sobre el flujo de datos. Ningún dispositivo cliente necesita acceso directo a su lógica de procesamiento de códigos de barras. Esta arquitectura respalda naturalmente su modelo de seguridad de confianza cero y al mismo tiempo permite una integración fluida con los sistemas empresariales existentes.
Empecemos con la base. Las funciones de lectura de IronBarcode admiten todos los principales formatos de códigos de barras que su empresa pueda encontrar. Desde los códigos de barras tradicionales Code 39 utilizados en fabricación hasta los códigos QR modernos en campañas de marketing, la biblioteca los gestiona todos. La guía de formatos de códigos de barras compatibles proporciona una referencia completa para planificar su implementación.
Lo que hace que esto sea particularmente valioso para escenarios empresariales son las capacidades de tolerancia a fallas de la biblioteca. Los códigos de barras del mundo real no son perfectos. Se dañan, se imprimen mal o se escanean en ángulos. El procesamiento avanzado de imágenes de IronBarcode maneja estos desafíos automáticamente, reduciendo los tickets de soporte y mejorando la eficiencia operativa.
¿Cómo instalar y configurar la biblioteca de códigos de barras?
Las implementaciones empresariales requieren una consideración cuidadosa de las dependencias y las licencias. IronBarcode simplifica esto con una instalación sencilla de NuGet y licencias transparentes. A continuación se explica cómo comenzar con su proyecto ASP.NET Core :
Install-Package IronBarCode
Install-Package IronBarCode
Para entornos empresariales con requisitos de plataforma específicos, consulte la guía de instalación avanzada de NuGet . Esto cubre escenarios como la orientación a versiones específicas de .NET o la optimización para plataformas particulares.
Una vez instalado, verifique su configuración con una prueba sencilla. Este código demuestra la capacidad de la biblioteca para leer códigos de barras con una configuración mínima:
using IronBarCode;
// Verify installation with a basic barcode read
public class InstallationTest
{
public static void VerifySetup()
{
try
{
// Test with a sample barcode image
var result = BarcodeReader.Read("test-barcode.png");
if (result.Any())
{
Console.WriteLine($"Success! Detected: {result.First().Value}");
Console.WriteLine($"Format: {result.First().BarcodeType}");
Console.WriteLine($"Confidence: {result.First().Confidence}%");
}
else
{
Console.WriteLine("No barcode detected in test image");
}
}
catch (Exception ex)
{
Console.WriteLine($"Setup issue: {ex.Message}");
}
}
}
using IronBarCode;
// Verify installation with a basic barcode read
public class InstallationTest
{
public static void VerifySetup()
{
try
{
// Test with a sample barcode image
var result = BarcodeReader.Read("test-barcode.png");
if (result.Any())
{
Console.WriteLine($"Success! Detected: {result.First().Value}");
Console.WriteLine($"Format: {result.First().BarcodeType}");
Console.WriteLine($"Confidence: {result.First().Confidence}%");
}
else
{
Console.WriteLine("No barcode detected in test image");
}
}
catch (Exception ex)
{
Console.WriteLine($"Setup issue: {ex.Message}");
}
}
}
Imports IronBarCode
' Verify installation with a basic barcode read
Public Class InstallationTest
Public Shared Sub VerifySetup()
Try
' Test with a sample barcode image
Dim result = BarcodeReader.Read("test-barcode.png")
If result.Any() Then
Console.WriteLine($"Success! Detected: {result.First().Value}")
Console.WriteLine($"Format: {result.First().BarcodeType}")
Console.WriteLine($"Confidence: {result.First().Confidence}%")
Else
Console.WriteLine("No barcode detected in test image")
End If
Catch ex As Exception
Console.WriteLine($"Setup issue: {ex.Message}")
End Try
End Sub
End Class
Este paso de verificación es crucial para las implementaciones empresariales. Confirma que la biblioteca está correctamente instalada, tiene licencia y puede acceder a los recursos necesarios del sistema. El resultado del puntaje de confianza ayuda a establecer expectativas de referencia para sus tipos de códigos de barras específicos.
Para implementaciones de producción, revise cuidadosamente la documentación de licencia . Las licencias empresariales admiten implementaciones ilimitadas dentro de su organización, lo cual es crucial para escalar en múltiples centros de datos. La guía de configuración de la clave de licencia explica varios métodos de activación, incluidas las variables de entorno y los archivos de configuración que se alinean con sus prácticas.
Compatibilidad de plataformas para empresas
Es probable que su empresa utilice un entorno heterogéneo. El desarrollo se realiza en Windows, el ensayo se ejecuta en contenedores Linux y algunos equipos pueden usar macOS. La compatibilidad multiplataforma de IronBarcode garantiza un comportamiento consistente en todas estas plataformas.
Para implementaciones en contenedores, la guía de configuración de Docker proporciona instrucciones específicas para contenedores Linux. Esto incluye el manejo de dependencias y la optimización del tamaño de la imagen para una implementación eficiente en la nube. Si está realizando implementaciones en Azure o AWS Lambda , las guías específicas de cada plataforma garantizan un rendimiento óptimo en estos entornos.
La biblioteca incluso admite escenarios móviles a través de la integración .NET MAUI , lo que le permite ampliar sus capacidades de escaneo de códigos de barras a dispositivos iOS y Android cuando los trabajadores de campo necesitan capacidades de escaneo móvil.
¿Cómo se construye un controlador API de escáner completo?
Construyamos una API de escáner lista para producción que aborde las necesidades empresariales reales. Esta implementación incluye manejo adecuado de errores, consideraciones de seguridad y optimizaciones de rendimiento:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using IronBarCode;
using System.Drawing;
using System.ComponentModel.DataAnnotations;
namespace BarcodeScannerAPI.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
[Authorize] // Require authentication for all endpoints
public class ScannerController : ControllerBase
{
private readonly ILogger<ScannerController> _logger;
private readonly IConfiguration _configuration;
public ScannerController(ILogger<ScannerController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
// Enhanced result model with audit fields
public class ScanResult
{
public bool Success { get; set; }
public List<BarcodeData> Barcodes { get; set; } = new List<BarcodeData>();
public string RequestId { get; set; } = Guid.NewGuid().ToString();
public DateTime ProcessedAt { get; set; } = DateTime.UtcNow;
public long ProcessingTimeMs { get; set; }
public string ErrorMessage { get; set; }
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
}
public class BarcodeData
{
public string Value { get; set; }
public string Format { get; set; }
public double Confidence { get; set; }
public Rectangle Location { get; set; }
public int PageNumber { get; set; } // For PDF processing
}
// Input validation model
public class ScanRequest
{
[Required]
public IFormFile File { get; set; }
[Range(1, 10)]
public int MaxBarcodes { get; set; } = 5;
public string[] ExpectedFormats { get; set; }
public bool EnableImageCorrection { get; set; } = true;
}
[HttpPost("scan")]
[RequestSizeLimit(52428800)] // 50MB limit
public async Task<ActionResult<ScanResult>> ScanDocument([FromForm] ScanRequest request)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var result = new ScanResult();
try
{
// Validate file type for security
var allowedExtensions = new[] { ".pdf", ".png", ".jpg", ".jpeg", ".gif", ".tiff", ".bmp" };
var fileExtension = Path.GetExtension(request.File.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(fileExtension))
{
_logger.LogWarning($"Rejected file type: {fileExtension}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Unsupported file type. Allowed: PDF, PNG, JPG, GIF, TIFF, BMP"
});
}
// Process the file
using var stream = new MemoryStream();
await request.File.CopyToAsync(stream);
var fileBytes = stream.ToArray();
// Log for audit trail
_logger.LogInformation($"Processing {request.File.FileName} ({fileBytes.Length} bytes) - RequestId: {result.RequestId}");
// Configure scanner based on requirements
var options = ConfigureOptions(request);
// Handle different file types
BarcodeResults scanResults;
if (fileExtension == ".pdf")
{
var pdfOptions = new PdfBarcodeReaderOptions
{
Scale = 3,
DPI = 300,
MaxThreads = 4
};
scanResults = BarcodeReader.ReadPdf(fileBytes, pdfOptions);
}
else
{
scanResults = BarcodeReader.Read(fileBytes, options);
}
// Process results
if (scanResults.Any())
{
result.Success = true;
foreach (var barcode in scanResults.Take(request.MaxBarcodes))
{
result.Barcodes.Add(new BarcodeData
{
Value = barcode.Value,
Format = barcode.BarcodeType.ToString(),
Confidence = barcode.Confidence,
Location = barcode.Bounds,
PageNumber = barcode.PageNumber
});
}
result.Metadata["TotalFound"] = scanResults.Count();
result.Metadata["FileSize"] = fileBytes.Length;
result.Metadata["FileName"] = request.File.FileName;
_logger.LogInformation($"Successfully scanned {scanResults.Count()} barcodes - RequestId: {result.RequestId}");
}
else
{
result.Success = false;
result.ErrorMessage = "No barcodes detected in the document";
_logger.LogWarning($"No barcodes found - RequestId: {result.RequestId}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Scanning error - RequestId: {result.RequestId}");
result.Success = false;
result.ErrorMessage = "An error occurred during processing";
// Don't expose internal errors to clients
if (_configuration.GetValue<bool>("DetailedErrors"))
{
result.Metadata["Exception"] = ex.Message;
}
}
finally
{
stopwatch.Stop();
result.ProcessingTimeMs = stopwatch.ElapsedMilliseconds;
}
return result;
}
private BarcodeReaderOptions ConfigureOptions(ScanRequest request)
{
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
ExpectMultipleBarcodes = request.MaxBarcodes > 1,
RemoveFalsePositive = true,
Multithreaded = true,
MaxParallelThreads = Environment.ProcessorCount
};
// Apply format filtering if specified
if (request.ExpectedFormats?.Any() == true)
{
var formats = BarcodeEncoding.None;
foreach (var format in request.ExpectedFormats)
{
if (Enum.TryParse<BarcodeEncoding>(format, true, out var encoding))
{
formats |= encoding;
}
}
options.ExpectBarcodeTypes = formats;
}
// Enable image correction for damaged barcodes
if (request.EnableImageCorrection)
{
options.ImageFilters = new ImageFilterCollection
{
new SharpenFilter(2),
new ContrastFilter(1.5f),
new BrightnessFilter(1.1f)
};
options.AutoRotate = true;
}
return options;
}
// Batch processing endpoint for high-volume scenarios
[HttpPost("scan-batch")]
[RequestSizeLimit(524288000)] // 500MB for batch operations
public async Task<ActionResult<List<ScanResult>>> ScanBatch(List<IFormFile> files)
{
if (files == null || !files.Any())
{
return BadRequest("No files provided");
}
if (files.Count > 50)
{
return BadRequest("Maximum 50 files per batch");
}
var tasks = files.Select(file => ProcessFileAsync(file));
var results = await Task.WhenAll(tasks);
_logger.LogInformation($"Batch processed {files.Count} files, {results.Count(r => r.Success)} successful");
return Ok(results);
}
private async Task<ScanResult> ProcessFileAsync(IFormFile file)
{
// Reuse the scanning logic
var request = new ScanRequest
{
File = file,
EnableImageCorrection = true
};
var actionResult = await ScanDocument(request);
if (actionResult.Result is OkObjectResult okResult)
{
return okResult.Value as ScanResult;
}
else if (actionResult.Result is BadRequestObjectResult badResult)
{
return badResult.Value as ScanResult;
}
return new ScanResult
{
Success = false,
ErrorMessage = "Processing failed"
};
}
}
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using IronBarCode;
using System.Drawing;
using System.ComponentModel.DataAnnotations;
namespace BarcodeScannerAPI.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
[Authorize] // Require authentication for all endpoints
public class ScannerController : ControllerBase
{
private readonly ILogger<ScannerController> _logger;
private readonly IConfiguration _configuration;
public ScannerController(ILogger<ScannerController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
// Enhanced result model with audit fields
public class ScanResult
{
public bool Success { get; set; }
public List<BarcodeData> Barcodes { get; set; } = new List<BarcodeData>();
public string RequestId { get; set; } = Guid.NewGuid().ToString();
public DateTime ProcessedAt { get; set; } = DateTime.UtcNow;
public long ProcessingTimeMs { get; set; }
public string ErrorMessage { get; set; }
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
}
public class BarcodeData
{
public string Value { get; set; }
public string Format { get; set; }
public double Confidence { get; set; }
public Rectangle Location { get; set; }
public int PageNumber { get; set; } // For PDF processing
}
// Input validation model
public class ScanRequest
{
[Required]
public IFormFile File { get; set; }
[Range(1, 10)]
public int MaxBarcodes { get; set; } = 5;
public string[] ExpectedFormats { get; set; }
public bool EnableImageCorrection { get; set; } = true;
}
[HttpPost("scan")]
[RequestSizeLimit(52428800)] // 50MB limit
public async Task<ActionResult<ScanResult>> ScanDocument([FromForm] ScanRequest request)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var result = new ScanResult();
try
{
// Validate file type for security
var allowedExtensions = new[] { ".pdf", ".png", ".jpg", ".jpeg", ".gif", ".tiff", ".bmp" };
var fileExtension = Path.GetExtension(request.File.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(fileExtension))
{
_logger.LogWarning($"Rejected file type: {fileExtension}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Unsupported file type. Allowed: PDF, PNG, JPG, GIF, TIFF, BMP"
});
}
// Process the file
using var stream = new MemoryStream();
await request.File.CopyToAsync(stream);
var fileBytes = stream.ToArray();
// Log for audit trail
_logger.LogInformation($"Processing {request.File.FileName} ({fileBytes.Length} bytes) - RequestId: {result.RequestId}");
// Configure scanner based on requirements
var options = ConfigureOptions(request);
// Handle different file types
BarcodeResults scanResults;
if (fileExtension == ".pdf")
{
var pdfOptions = new PdfBarcodeReaderOptions
{
Scale = 3,
DPI = 300,
MaxThreads = 4
};
scanResults = BarcodeReader.ReadPdf(fileBytes, pdfOptions);
}
else
{
scanResults = BarcodeReader.Read(fileBytes, options);
}
// Process results
if (scanResults.Any())
{
result.Success = true;
foreach (var barcode in scanResults.Take(request.MaxBarcodes))
{
result.Barcodes.Add(new BarcodeData
{
Value = barcode.Value,
Format = barcode.BarcodeType.ToString(),
Confidence = barcode.Confidence,
Location = barcode.Bounds,
PageNumber = barcode.PageNumber
});
}
result.Metadata["TotalFound"] = scanResults.Count();
result.Metadata["FileSize"] = fileBytes.Length;
result.Metadata["FileName"] = request.File.FileName;
_logger.LogInformation($"Successfully scanned {scanResults.Count()} barcodes - RequestId: {result.RequestId}");
}
else
{
result.Success = false;
result.ErrorMessage = "No barcodes detected in the document";
_logger.LogWarning($"No barcodes found - RequestId: {result.RequestId}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Scanning error - RequestId: {result.RequestId}");
result.Success = false;
result.ErrorMessage = "An error occurred during processing";
// Don't expose internal errors to clients
if (_configuration.GetValue<bool>("DetailedErrors"))
{
result.Metadata["Exception"] = ex.Message;
}
}
finally
{
stopwatch.Stop();
result.ProcessingTimeMs = stopwatch.ElapsedMilliseconds;
}
return result;
}
private BarcodeReaderOptions ConfigureOptions(ScanRequest request)
{
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
ExpectMultipleBarcodes = request.MaxBarcodes > 1,
RemoveFalsePositive = true,
Multithreaded = true,
MaxParallelThreads = Environment.ProcessorCount
};
// Apply format filtering if specified
if (request.ExpectedFormats?.Any() == true)
{
var formats = BarcodeEncoding.None;
foreach (var format in request.ExpectedFormats)
{
if (Enum.TryParse<BarcodeEncoding>(format, true, out var encoding))
{
formats |= encoding;
}
}
options.ExpectBarcodeTypes = formats;
}
// Enable image correction for damaged barcodes
if (request.EnableImageCorrection)
{
options.ImageFilters = new ImageFilterCollection
{
new SharpenFilter(2),
new ContrastFilter(1.5f),
new BrightnessFilter(1.1f)
};
options.AutoRotate = true;
}
return options;
}
// Batch processing endpoint for high-volume scenarios
[HttpPost("scan-batch")]
[RequestSizeLimit(524288000)] // 500MB for batch operations
public async Task<ActionResult<List<ScanResult>>> ScanBatch(List<IFormFile> files)
{
if (files == null || !files.Any())
{
return BadRequest("No files provided");
}
if (files.Count > 50)
{
return BadRequest("Maximum 50 files per batch");
}
var tasks = files.Select(file => ProcessFileAsync(file));
var results = await Task.WhenAll(tasks);
_logger.LogInformation($"Batch processed {files.Count} files, {results.Count(r => r.Success)} successful");
return Ok(results);
}
private async Task<ScanResult> ProcessFileAsync(IFormFile file)
{
// Reuse the scanning logic
var request = new ScanRequest
{
File = file,
EnableImageCorrection = true
};
var actionResult = await ScanDocument(request);
if (actionResult.Result is OkObjectResult okResult)
{
return okResult.Value as ScanResult;
}
else if (actionResult.Result is BadRequestObjectResult badResult)
{
return badResult.Value as ScanResult;
}
return new ScanResult
{
Success = false,
ErrorMessage = "Processing failed"
};
}
}
}
Imports Microsoft.AspNetCore.Mvc
Imports Microsoft.AspNetCore.Authorization
Imports IronBarCode
Imports System.Drawing
Imports System.ComponentModel.DataAnnotations
Namespace BarcodeScannerAPI.Controllers
<ApiController>
<Route("api/v1/[controller]")>
<Authorize>
Public Class ScannerController
Inherits ControllerBase
Private ReadOnly _logger As ILogger(Of ScannerController)
Private ReadOnly _configuration As IConfiguration
Public Sub New(logger As ILogger(Of ScannerController), configuration As IConfiguration)
_logger = logger
_configuration = configuration
End Sub
' Enhanced result model with audit fields
Public Class ScanResult
Public Property Success As Boolean
Public Property Barcodes As List(Of BarcodeData) = New List(Of BarcodeData)()
Public Property RequestId As String = Guid.NewGuid().ToString()
Public Property ProcessedAt As DateTime = DateTime.UtcNow
Public Property ProcessingTimeMs As Long
Public Property ErrorMessage As String
Public Property Metadata As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()
End Class
Public Class BarcodeData
Public Property Value As String
Public Property Format As String
Public Property Confidence As Double
Public Property Location As Rectangle
Public Property PageNumber As Integer
End Class
' Input validation model
Public Class ScanRequest
<Required>
Public Property File As IFormFile
<Range(1, 10)>
Public Property MaxBarcodes As Integer = 5
Public Property ExpectedFormats As String()
Public Property EnableImageCorrection As Boolean = True
End Class
<HttpPost("scan")>
<RequestSizeLimit(52428800)>
Public Async Function ScanDocument(<FromForm> request As ScanRequest) As Task(Of ActionResult(Of ScanResult))
Dim stopwatch = System.Diagnostics.Stopwatch.StartNew()
Dim result = New ScanResult()
Try
' Validate file type for security
Dim allowedExtensions = {".pdf", ".png", ".jpg", ".jpeg", ".gif", ".tiff", ".bmp"}
Dim fileExtension = Path.GetExtension(request.File.FileName).ToLowerInvariant()
If Not allowedExtensions.Contains(fileExtension) Then
_logger.LogWarning($"Rejected file type: {fileExtension}")
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Unsupported file type. Allowed: PDF, PNG, JPG, GIF, TIFF, BMP"
})
End If
' Process the file
Using stream = New MemoryStream()
Await request.File.CopyToAsync(stream)
Dim fileBytes = stream.ToArray()
' Log for audit trail
_logger.LogInformation($"Processing {request.File.FileName} ({fileBytes.Length} bytes) - RequestId: {result.RequestId}")
' Configure scanner based on requirements
Dim options = ConfigureOptions(request)
' Handle different file types
Dim scanResults As BarcodeResults
If fileExtension = ".pdf" Then
Dim pdfOptions = New PdfBarcodeReaderOptions With {
.Scale = 3,
.DPI = 300,
.MaxThreads = 4
}
scanResults = BarcodeReader.ReadPdf(fileBytes, pdfOptions)
Else
scanResults = BarcodeReader.Read(fileBytes, options)
End If
' Process results
If scanResults.Any() Then
result.Success = True
For Each barcode In scanResults.Take(request.MaxBarcodes)
result.Barcodes.Add(New BarcodeData With {
.Value = barcode.Value,
.Format = barcode.BarcodeType.ToString(),
.Confidence = barcode.Confidence,
.Location = barcode.Bounds,
.PageNumber = barcode.PageNumber
})
Next
result.Metadata("TotalFound") = scanResults.Count()
result.Metadata("FileSize") = fileBytes.Length
result.Metadata("FileName") = request.File.FileName
_logger.LogInformation($"Successfully scanned {scanResults.Count()} barcodes - RequestId: {result.RequestId}")
Else
result.Success = False
result.ErrorMessage = "No barcodes detected in the document"
_logger.LogWarning($"No barcodes found - RequestId: {result.RequestId}")
End If
End Using
Catch ex As Exception
_logger.LogError(ex, $"Scanning error - RequestId: {result.RequestId}")
result.Success = False
result.ErrorMessage = "An error occurred during processing"
' Don't expose internal errors to clients
If _configuration.GetValue(Of Boolean)("DetailedErrors") Then
result.Metadata("Exception") = ex.Message
End If
Finally
stopwatch.Stop()
result.ProcessingTimeMs = stopwatch.ElapsedMilliseconds
End Try
Return result
End Function
Private Function ConfigureOptions(request As ScanRequest) As BarcodeReaderOptions
Dim options = New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Balanced,
.ExpectMultipleBarcodes = request.MaxBarcodes > 1,
.RemoveFalsePositive = True,
.Multithreaded = True,
.MaxParallelThreads = Environment.ProcessorCount
}
' Apply format filtering if specified
If request.ExpectedFormats?.Any() = True Then
Dim formats = BarcodeEncoding.None
For Each format In request.ExpectedFormats
Dim encoding As BarcodeEncoding
If [Enum].TryParse(format, True, encoding) Then
formats = formats Or encoding
End If
Next
options.ExpectBarcodeTypes = formats
End If
' Enable image correction for damaged barcodes
If request.EnableImageCorrection Then
options.ImageFilters = New ImageFilterCollection From {
New SharpenFilter(2),
New ContrastFilter(1.5F),
New BrightnessFilter(1.1F)
}
options.AutoRotate = True
End If
Return options
End Function
' Batch processing endpoint for high-volume scenarios
<HttpPost("scan-batch")>
<RequestSizeLimit(524288000)>
Public Async Function ScanBatch(files As List(Of IFormFile)) As Task(Of ActionResult(Of List(Of ScanResult)))
If files Is Nothing OrElse Not files.Any() Then
Return BadRequest("No files provided")
End If
If files.Count > 50 Then
Return BadRequest("Maximum 50 files per batch")
End If
Dim tasks = files.Select(Function(file) ProcessFileAsync(file))
Dim results = Await Task.WhenAll(tasks)
_logger.LogInformation($"Batch processed {files.Count} files, {results.Count(Function(r) r.Success)} successful")
Return Ok(results)
End Function
Private Async Function ProcessFileAsync(file As IFormFile) As Task(Of ScanResult)
' Reuse the scanning logic
Dim request = New ScanRequest With {
.File = file,
.EnableImageCorrection = True
}
Dim actionResult = Await ScanDocument(request)
If TypeOf actionResult.Result Is OkObjectResult Then
Return DirectCast(actionResult.Result, OkObjectResult).Value
ElseIf TypeOf actionResult.Result Is BadRequestObjectResult Then
Return DirectCast(actionResult.Result, BadRequestObjectResult).Value
End If
Return New ScanResult With {
.Success = False,
.ErrorMessage = "Processing failed"
}
End Function
End Class
End Namespace
Esta implementación profesional incluye varias funciones críticas que requieren las implementaciones de producción. La autenticación garantiza que solo los usuarios autorizados puedan acceder al servicio de escaneo. Los límites de tamaño de las solicitudes previenen ataques de denegación de servicio. El registro completo proporciona la pista de auditoría que necesitan los equipos de cumplimiento.
El modelo de respuesta estructurada no sólo devuelve valores de código de barras, sino también puntajes de confianza y datos de ubicación. Estos metadatos resultan invaluables para los procesos de garantía de calidad. Cuando el código de barras de un proveedor se escanea con poca confianza, su sistema puede marcarlo para revisión manual.
Manejo profesional de errores
¿Observa cómo el controlador nunca expone detalles de excepciones internas a los clientes? Esto sigue las mejores prácticas de seguridad al prevenir la fuga de información. El registro detallado captura todo lo necesario para la depuración y mantiene las respuestas de error genéricas.
El campo RequestId permite el seguimiento de extremo a extremo en sus sistemas distribuidos. Cuando un trabajador del almacén informa un problema de escaneo, su equipo de soporte puede localizar rápidamente la solicitud exacta en su sistema de registro centralizado. Esto reduce drásticamente el tiempo medio de resolución (MTTR) de problemas de producción.
La supervisión del rendimiento a través de ProcessingTimeMs ayuda a identificar cuellos de botella. Si ciertos tipos de códigos de barras tardan constantemente más en procesarse, puede ajustar la configuración de velocidad de lectura en consecuencia. El ejemplo de velocidades de lectura demuestra cómo las diferentes configuraciones afectan el rendimiento.
¿Cómo manejar diferentes fuentes de entrada?
Los sistemas empresariales rara vez se estandarizan en un único formato. Su API de escáner debe gestionar todo, desde cámaras de almacén de alta resolución hasta escaneos de PDF de baja calidad de máquinas de fax con décadas de antigüedad. A continuación se explica cómo desarrollar esa flexibilidad:
// Extended controller with multiple input handlers
[ApiController]
[Route("api/v1/[controller]")]
[Authorize]
public class AdvancedScannerController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<AdvancedScannerController> _logger;
public AdvancedScannerController(
IHttpClientFactory httpClientFactory,
ILogger<AdvancedScannerController> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
// Handle Base64 input (common in web applications)
[HttpPost("scan-base64")]
public ActionResult<ScanResult> ScanBase64([FromBody] Base64Request request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
// Validate Base64 format
var base64Data = request.ImageData;
if (base64Data.Contains(","))
{
// Handle data URL format: "data:image/png;base64,..."
base64Data = base64Data.Substring(base64Data.IndexOf(",") + 1);
}
var imageBytes = Convert.FromBase64String(base64Data);
// Implement size validation
if (imageBytes.Length > 10 * 1024 * 1024) // 10MB limit
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Image size exceeds 10MB limit"
});
}
// Configure for web-uploaded images (often lower quality)
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Detailed,
ImageFilters = new ImageFilterCollection
{
new SharpenFilter(3),
new ContrastFilter(2),
new DeNoise()
},
TryInvertColor = true, // Handle inverted barcodes
AutoRotate = true
};
var results = BarcodeReader.Read(imageBytes, options);
return ProcessResults(results, "base64", request.FileName);
}
catch (FormatException)
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Invalid Base64 format"
});
}
}
// Handle URL input (for cloud storage integration)
[HttpPost("scan-url")]
public async Task<ActionResult<ScanResult>> ScanFromUrl([FromBody] UrlScanRequest request)
{
if (!Uri.TryCreate(request.Url, UriKind.Absolute, out var uri))
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Invalid URL format"
});
}
// Validate URL domain (security measure)
var allowedDomains = _configuration.GetSection("AllowedDomains").Get<string[]>()
?? new[] { "blob.core.windows.net", "s3.amazonaws.com" };
if (!allowedDomains.Any(domain => uri.Host.EndsWith(domain)))
{
_logger.LogWarning($"Rejected URL from unauthorized domain: {uri.Host}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "URL domain not authorized"
});
}
try
{
using var httpClient = _httpClientFactory.CreateClient("BarcodeScanner");
httpClient.Timeout = TimeSpan.FromSeconds(30);
// Add headers to avoid being blocked
httpClient.DefaultRequestHeaders.Add("User-Agent", "BarcodeScannerAPI/2.0");
var response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
// Check content type
var contentType = response.Content.Headers.ContentType?.MediaType;
if (!IsValidContentType(contentType))
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = $"Unsupported content type: {contentType}"
});
}
var imageBytes = await response.Content.ReadAsByteArrayAsync();
// Use async processing for better scalability
var results = await BarcodeReader.ReadAsync(imageBytes);
return ProcessResults(results, "url", uri.ToString());
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, $"Failed to download from URL: {uri}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Failed to download image from URL"
});
}
catch (TaskCanceledException)
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Download timeout - file too large or slow connection"
});
}
}
// Handle multi-page PDFs with page-specific processing
[HttpPost("scan-pdf-advanced")]
public async Task<ActionResult<PdfScanResult>> ScanPdfAdvanced([FromForm] PdfScanRequest request)
{
using var stream = new MemoryStream();
await request.File.CopyToAsync(stream);
var pdfOptions = new PdfBarcodeReaderOptions
{
Scale = request.Scale ?? 3,
DPI = request.DPI ?? 300,
PageNumbers = request.PageNumbers,
Password = request.Password,
MaxThreads = Math.Min(request.MaxThreads ?? 4, Environment.ProcessorCount)
};
// For large PDFs, process in chunks
if (stream.Length > 50 * 1024 * 1024) // 50MB
{
_logger.LogInformation($"Large PDF detected ({stream.Length / 1024 / 1024}MB), using chunked processing");
pdfOptions.MaxThreads = 2; // Reduce memory pressure
}
var results = BarcodeReader.ReadPdf(stream.ToArray(), pdfOptions);
// Group results by page
var pageResults = results.GroupBy(r => r.PageNumber)
.OrderBy(g => g.Key)
.Select(g => new PageBarcodeResult
{
PageNumber = g.Key,
BarcodeCount = g.Count(),
Barcodes = g.Select(b => new BarcodeData
{
Value = b.Value,
Format = b.BarcodeType.ToString(),
Confidence = b.Confidence,
Location = b.Bounds
}).ToList()
}).ToList();
return Ok(new PdfScanResult
{
Success = true,
TotalPages = pageResults.Count,
TotalBarcodes = results.Count(),
PageResults = pageResults,
RequestId = Guid.NewGuid().ToString()
});
}
// Handle damaged or low-quality barcodes
[HttpPost("scan-damaged")]
public async Task<ActionResult<ScanResult>> ScanDamagedBarcode([FromForm] IFormFile file)
{
using var stream = new MemoryStream();
await file.CopyToAsync(stream);
// Aggressive image correction for damaged barcodes
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.ExtremeDetail,
ImageFilters = new ImageFilterCollection
{
new SharpenFilter(4),
new ContrastFilter(3),
new BrightnessFilter(1.5f),
new BinaryThresholdFilter(128),
new DeNoise(3)
},
TryInvertColor = true,
AutoRotate = true,
UseCode39ExtendedMode = true,
RemoveFalsePositive = false, // Accept lower confidence
Confidence = ConfidenceLevel.Low
};
var results = BarcodeReader.Read(stream.ToArray(), options);
// If still no results, try with different threshold
if (!results.Any())
{
options.ImageFilters = new ImageFilterCollection
{
new AdaptiveThresholdFilter(9),
new MedianFilter(3),
new Dilate(1)
};
results = BarcodeReader.Read(stream.ToArray(), options);
}
return ProcessResults(results, "damaged", file.FileName);
}
private bool IsValidContentType(string contentType)
{
var validTypes = new[]
{
"image/jpeg", "image/jpg", "image/png", "image/gif",
"image/tiff", "image/bmp", "application/pdf"
};
return validTypes.Contains(contentType?.ToLower());
}
private ActionResult<ScanResult> ProcessResults(BarcodeResults results, string source, string identifier)
{
var scanResult = new ScanResult
{
Metadata = new Dictionary<string, object>
{
["Source"] = source,
["Identifier"] = identifier,
["ProcessedAt"] = DateTime.UtcNow
}
};
if (results.Any())
{
scanResult.Success = true;
scanResult.Barcodes = results.Select(r => new BarcodeData
{
Value = r.Value,
Format = r.BarcodeType.ToString(),
Confidence = r.Confidence,
Location = r.Bounds
}).ToList();
// Log low confidence results for quality monitoring
var lowConfidence = results.Where(r => r.Confidence < 70).ToList();
if (lowConfidence.Any())
{
_logger.LogWarning($"Low confidence barcodes detected: {lowConfidence.Count} of {results.Count()} from {identifier}");
}
}
else
{
scanResult.Success = false;
scanResult.ErrorMessage = "No barcodes detected";
}
return Ok(scanResult);
}
}
// Request models
public class Base64Request
{
[Required]
public string ImageData { get; set; }
public string FileName { get; set; }
}
public class UrlScanRequest
{
[Required]
[Url]
public string Url { get; set; }
}
public class PdfScanRequest
{
[Required]
public IFormFile File { get; set; }
public int? Scale { get; set; }
public int? DPI { get; set; }
public int[] PageNumbers { get; set; }
public string Password { get; set; }
public int? MaxThreads { get; set; }
}
// Response models
public class PdfScanResult : ScanResult
{
public int TotalPages { get; set; }
public int TotalBarcodes { get; set; }
public List<PageBarcodeResult> PageResults { get; set; }
}
public class PageBarcodeResult
{
public int PageNumber { get; set; }
public int BarcodeCount { get; set; }
public List<BarcodeData> Barcodes { get; set; }
}
// Extended controller with multiple input handlers
[ApiController]
[Route("api/v1/[controller]")]
[Authorize]
public class AdvancedScannerController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<AdvancedScannerController> _logger;
public AdvancedScannerController(
IHttpClientFactory httpClientFactory,
ILogger<AdvancedScannerController> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
// Handle Base64 input (common in web applications)
[HttpPost("scan-base64")]
public ActionResult<ScanResult> ScanBase64([FromBody] Base64Request request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
// Validate Base64 format
var base64Data = request.ImageData;
if (base64Data.Contains(","))
{
// Handle data URL format: "data:image/png;base64,..."
base64Data = base64Data.Substring(base64Data.IndexOf(",") + 1);
}
var imageBytes = Convert.FromBase64String(base64Data);
// Implement size validation
if (imageBytes.Length > 10 * 1024 * 1024) // 10MB limit
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Image size exceeds 10MB limit"
});
}
// Configure for web-uploaded images (often lower quality)
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Detailed,
ImageFilters = new ImageFilterCollection
{
new SharpenFilter(3),
new ContrastFilter(2),
new DeNoise()
},
TryInvertColor = true, // Handle inverted barcodes
AutoRotate = true
};
var results = BarcodeReader.Read(imageBytes, options);
return ProcessResults(results, "base64", request.FileName);
}
catch (FormatException)
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Invalid Base64 format"
});
}
}
// Handle URL input (for cloud storage integration)
[HttpPost("scan-url")]
public async Task<ActionResult<ScanResult>> ScanFromUrl([FromBody] UrlScanRequest request)
{
if (!Uri.TryCreate(request.Url, UriKind.Absolute, out var uri))
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Invalid URL format"
});
}
// Validate URL domain (security measure)
var allowedDomains = _configuration.GetSection("AllowedDomains").Get<string[]>()
?? new[] { "blob.core.windows.net", "s3.amazonaws.com" };
if (!allowedDomains.Any(domain => uri.Host.EndsWith(domain)))
{
_logger.LogWarning($"Rejected URL from unauthorized domain: {uri.Host}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "URL domain not authorized"
});
}
try
{
using var httpClient = _httpClientFactory.CreateClient("BarcodeScanner");
httpClient.Timeout = TimeSpan.FromSeconds(30);
// Add headers to avoid being blocked
httpClient.DefaultRequestHeaders.Add("User-Agent", "BarcodeScannerAPI/2.0");
var response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
// Check content type
var contentType = response.Content.Headers.ContentType?.MediaType;
if (!IsValidContentType(contentType))
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = $"Unsupported content type: {contentType}"
});
}
var imageBytes = await response.Content.ReadAsByteArrayAsync();
// Use async processing for better scalability
var results = await BarcodeReader.ReadAsync(imageBytes);
return ProcessResults(results, "url", uri.ToString());
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, $"Failed to download from URL: {uri}");
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Failed to download image from URL"
});
}
catch (TaskCanceledException)
{
return BadRequest(new ScanResult
{
Success = false,
ErrorMessage = "Download timeout - file too large or slow connection"
});
}
}
// Handle multi-page PDFs with page-specific processing
[HttpPost("scan-pdf-advanced")]
public async Task<ActionResult<PdfScanResult>> ScanPdfAdvanced([FromForm] PdfScanRequest request)
{
using var stream = new MemoryStream();
await request.File.CopyToAsync(stream);
var pdfOptions = new PdfBarcodeReaderOptions
{
Scale = request.Scale ?? 3,
DPI = request.DPI ?? 300,
PageNumbers = request.PageNumbers,
Password = request.Password,
MaxThreads = Math.Min(request.MaxThreads ?? 4, Environment.ProcessorCount)
};
// For large PDFs, process in chunks
if (stream.Length > 50 * 1024 * 1024) // 50MB
{
_logger.LogInformation($"Large PDF detected ({stream.Length / 1024 / 1024}MB), using chunked processing");
pdfOptions.MaxThreads = 2; // Reduce memory pressure
}
var results = BarcodeReader.ReadPdf(stream.ToArray(), pdfOptions);
// Group results by page
var pageResults = results.GroupBy(r => r.PageNumber)
.OrderBy(g => g.Key)
.Select(g => new PageBarcodeResult
{
PageNumber = g.Key,
BarcodeCount = g.Count(),
Barcodes = g.Select(b => new BarcodeData
{
Value = b.Value,
Format = b.BarcodeType.ToString(),
Confidence = b.Confidence,
Location = b.Bounds
}).ToList()
}).ToList();
return Ok(new PdfScanResult
{
Success = true,
TotalPages = pageResults.Count,
TotalBarcodes = results.Count(),
PageResults = pageResults,
RequestId = Guid.NewGuid().ToString()
});
}
// Handle damaged or low-quality barcodes
[HttpPost("scan-damaged")]
public async Task<ActionResult<ScanResult>> ScanDamagedBarcode([FromForm] IFormFile file)
{
using var stream = new MemoryStream();
await file.CopyToAsync(stream);
// Aggressive image correction for damaged barcodes
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.ExtremeDetail,
ImageFilters = new ImageFilterCollection
{
new SharpenFilter(4),
new ContrastFilter(3),
new BrightnessFilter(1.5f),
new BinaryThresholdFilter(128),
new DeNoise(3)
},
TryInvertColor = true,
AutoRotate = true,
UseCode39ExtendedMode = true,
RemoveFalsePositive = false, // Accept lower confidence
Confidence = ConfidenceLevel.Low
};
var results = BarcodeReader.Read(stream.ToArray(), options);
// If still no results, try with different threshold
if (!results.Any())
{
options.ImageFilters = new ImageFilterCollection
{
new AdaptiveThresholdFilter(9),
new MedianFilter(3),
new Dilate(1)
};
results = BarcodeReader.Read(stream.ToArray(), options);
}
return ProcessResults(results, "damaged", file.FileName);
}
private bool IsValidContentType(string contentType)
{
var validTypes = new[]
{
"image/jpeg", "image/jpg", "image/png", "image/gif",
"image/tiff", "image/bmp", "application/pdf"
};
return validTypes.Contains(contentType?.ToLower());
}
private ActionResult<ScanResult> ProcessResults(BarcodeResults results, string source, string identifier)
{
var scanResult = new ScanResult
{
Metadata = new Dictionary<string, object>
{
["Source"] = source,
["Identifier"] = identifier,
["ProcessedAt"] = DateTime.UtcNow
}
};
if (results.Any())
{
scanResult.Success = true;
scanResult.Barcodes = results.Select(r => new BarcodeData
{
Value = r.Value,
Format = r.BarcodeType.ToString(),
Confidence = r.Confidence,
Location = r.Bounds
}).ToList();
// Log low confidence results for quality monitoring
var lowConfidence = results.Where(r => r.Confidence < 70).ToList();
if (lowConfidence.Any())
{
_logger.LogWarning($"Low confidence barcodes detected: {lowConfidence.Count} of {results.Count()} from {identifier}");
}
}
else
{
scanResult.Success = false;
scanResult.ErrorMessage = "No barcodes detected";
}
return Ok(scanResult);
}
}
// Request models
public class Base64Request
{
[Required]
public string ImageData { get; set; }
public string FileName { get; set; }
}
public class UrlScanRequest
{
[Required]
[Url]
public string Url { get; set; }
}
public class PdfScanRequest
{
[Required]
public IFormFile File { get; set; }
public int? Scale { get; set; }
public int? DPI { get; set; }
public int[] PageNumbers { get; set; }
public string Password { get; set; }
public int? MaxThreads { get; set; }
}
// Response models
public class PdfScanResult : ScanResult
{
public int TotalPages { get; set; }
public int TotalBarcodes { get; set; }
public List<PageBarcodeResult> PageResults { get; set; }
}
public class PageBarcodeResult
{
public int PageNumber { get; set; }
public int BarcodeCount { get; set; }
public List<BarcodeData> Barcodes { get; set; }
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Net.Http
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Authorization
Imports Microsoft.AspNetCore.Http
Imports Microsoft.AspNetCore.Mvc
Imports Microsoft.Extensions.Configuration
Imports Microsoft.Extensions.Logging
<ApiController>
<Route("api/v1/[controller]")>
<Authorize>
Public Class AdvancedScannerController
Inherits ControllerBase
Private ReadOnly _httpClientFactory As IHttpClientFactory
Private ReadOnly _logger As ILogger(Of AdvancedScannerController)
Private ReadOnly _configuration As IConfiguration
Public Sub New(httpClientFactory As IHttpClientFactory, logger As ILogger(Of AdvancedScannerController), configuration As IConfiguration)
_httpClientFactory = httpClientFactory
_logger = logger
_configuration = configuration
End Sub
' Handle Base64 input (common in web applications)
<HttpPost("scan-base64")>
Public Function ScanBase64(<FromBody> request As Base64Request) As ActionResult(Of ScanResult)
If Not ModelState.IsValid Then
Return BadRequest(ModelState)
End If
Try
' Validate Base64 format
Dim base64Data = request.ImageData
If base64Data.Contains(",") Then
' Handle data URL format: "data:image/png;base64,..."
base64Data = base64Data.Substring(base64Data.IndexOf(",") + 1)
End If
Dim imageBytes = Convert.FromBase64String(base64Data)
' Implement size validation
If imageBytes.Length > 10 * 1024 * 1024 Then ' 10MB limit
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Image size exceeds 10MB limit"
})
End If
' Configure for web-uploaded images (often lower quality)
Dim options = New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Detailed,
.ImageFilters = New ImageFilterCollection From {
New SharpenFilter(3),
New ContrastFilter(2),
New DeNoise()
},
.TryInvertColor = True, ' Handle inverted barcodes
.AutoRotate = True
}
Dim results = BarcodeReader.Read(imageBytes, options)
Return ProcessResults(results, "base64", request.FileName)
Catch ex As FormatException
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Invalid Base64 format"
})
End Try
End Function
' Handle URL input (for cloud storage integration)
<HttpPost("scan-url")>
Public Async Function ScanFromUrl(<FromBody> request As UrlScanRequest) As Task(Of ActionResult(Of ScanResult))
Dim uri As Uri = Nothing
If Not Uri.TryCreate(request.Url, UriKind.Absolute, uri) Then
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Invalid URL format"
})
End If
' Validate URL domain (security measure)
Dim allowedDomains = _configuration.GetSection("AllowedDomains").Get(Of String())() _
?? New String() {"blob.core.windows.net", "s3.amazonaws.com"}
If Not allowedDomains.Any(Function(domain) uri.Host.EndsWith(domain)) Then
_logger.LogWarning($"Rejected URL from unauthorized domain: {uri.Host}")
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "URL domain not authorized"
})
End If
Try
Using httpClient = _httpClientFactory.CreateClient("BarcodeScanner")
httpClient.Timeout = TimeSpan.FromSeconds(30)
' Add headers to avoid being blocked
httpClient.DefaultRequestHeaders.Add("User-Agent", "BarcodeScannerAPI/2.0")
Dim response = Await httpClient.GetAsync(uri)
response.EnsureSuccessStatusCode()
' Check content type
Dim contentType = response.Content.Headers.ContentType?.MediaType
If Not IsValidContentType(contentType) Then
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = $"Unsupported content type: {contentType}"
})
End If
Dim imageBytes = Await response.Content.ReadAsByteArrayAsync()
' Use async processing for better scalability
Dim results = Await BarcodeReader.ReadAsync(imageBytes)
Return ProcessResults(results, "url", uri.ToString())
End Using
Catch ex As HttpRequestException
_logger.LogError(ex, $"Failed to download from URL: {uri}")
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Failed to download image from URL"
})
Catch ex As TaskCanceledException
Return BadRequest(New ScanResult With {
.Success = False,
.ErrorMessage = "Download timeout - file too large or slow connection"
})
End Try
End Function
' Handle multi-page PDFs with page-specific processing
<HttpPost("scan-pdf-advanced")>
Public Async Function ScanPdfAdvanced(<FromForm> request As PdfScanRequest) As Task(Of ActionResult(Of PdfScanResult))
Using stream = New MemoryStream()
Await request.File.CopyToAsync(stream)
Dim pdfOptions = New PdfBarcodeReaderOptions With {
.Scale = If(request.Scale, 3),
.DPI = If(request.DPI, 300),
.PageNumbers = request.PageNumbers,
.Password = request.Password,
.MaxThreads = Math.Min(If(request.MaxThreads, 4), Environment.ProcessorCount)
}
' For large PDFs, process in chunks
If stream.Length > 50 * 1024 * 1024 Then ' 50MB
_logger.LogInformation($"Large PDF detected ({stream.Length / 1024 / 1024}MB), using chunked processing")
pdfOptions.MaxThreads = 2 ' Reduce memory pressure
End If
Dim results = BarcodeReader.ReadPdf(stream.ToArray(), pdfOptions)
' Group results by page
Dim pageResults = results.GroupBy(Function(r) r.PageNumber) _
.OrderBy(Function(g) g.Key) _
.Select(Function(g) New PageBarcodeResult With {
.PageNumber = g.Key,
.BarcodeCount = g.Count(),
.Barcodes = g.Select(Function(b) New BarcodeData With {
.Value = b.Value,
.Format = b.BarcodeType.ToString(),
.Confidence = b.Confidence,
.Location = b.Bounds
}).ToList()
}).ToList()
Return Ok(New PdfScanResult With {
.Success = True,
.TotalPages = pageResults.Count,
.TotalBarcodes = results.Count(),
.PageResults = pageResults,
.RequestId = Guid.NewGuid().ToString()
})
End Using
End Function
' Handle damaged or low-quality barcodes
<HttpPost("scan-damaged")>
Public Async Function ScanDamagedBarcode(<FromForm> file As IFormFile) As Task(Of ActionResult(Of ScanResult))
Using stream = New MemoryStream()
Await file.CopyToAsync(stream)
' Aggressive image correction for damaged barcodes
Dim options = New BarcodeReaderOptions With {
.Speed = ReadingSpeed.ExtremeDetail,
.ImageFilters = New ImageFilterCollection From {
New SharpenFilter(4),
New ContrastFilter(3),
New BrightnessFilter(1.5F),
New BinaryThresholdFilter(128),
New DeNoise(3)
},
.TryInvertColor = True,
.AutoRotate = True,
.UseCode39ExtendedMode = True,
.RemoveFalsePositive = False, ' Accept lower confidence
.Confidence = ConfidenceLevel.Low
}
Dim results = BarcodeReader.Read(stream.ToArray(), options)
' If still no results, try with different threshold
If Not results.Any() Then
options.ImageFilters = New ImageFilterCollection From {
New AdaptiveThresholdFilter(9),
New MedianFilter(3),
New Dilate(1)
}
results = BarcodeReader.Read(stream.ToArray(), options)
End If
Return ProcessResults(results, "damaged", file.FileName)
End Using
End Function
Private Function IsValidContentType(contentType As String) As Boolean
Dim validTypes = New String() {
"image/jpeg", "image/jpg", "image/png", "image/gif",
"image/tiff", "image/bmp", "application/pdf"
}
Return validTypes.Contains(contentType?.ToLower())
End Function
Private Function ProcessResults(results As BarcodeResults, source As String, identifier As String) As ActionResult(Of ScanResult)
Dim scanResult = New ScanResult With {
.Metadata = New Dictionary(Of String, Object) From {
{"Source", source},
{"Identifier", identifier},
{"ProcessedAt", DateTime.UtcNow}
}
}
If results.Any() Then
scanResult.Success = True
scanResult.Barcodes = results.Select(Function(r) New BarcodeData With {
.Value = r.Value,
.Format = r.BarcodeType.ToString(),
.Confidence = r.Confidence,
.Location = r.Bounds
}).ToList()
' Log low confidence results for quality monitoring
Dim lowConfidence = results.Where(Function(r) r.Confidence < 70).ToList()
If lowConfidence.Any() Then
_logger.LogWarning($"Low confidence barcodes detected: {lowConfidence.Count} of {results.Count()} from {identifier}")
End If
Else
scanResult.Success = False
scanResult.ErrorMessage = "No barcodes detected"
End If
Return Ok(scanResult)
End Function
End Class
' Request models
Public Class Base64Request
<Required>
Public Property ImageData As String
Public Property FileName As String
End Class
Public Class UrlScanRequest
<Required>
<Url>
Public Property Url As String
End Class
Public Class PdfScanRequest
<Required>
Public Property File As IFormFile
Public Property Scale As Integer?
Public Property DPI As Integer?
Public Property PageNumbers As Integer()
Public Property Password As String
Public Property MaxThreads As Integer?
End Class
' Response models
Public Class PdfScanResult
Inherits ScanResult
Public Property TotalPages As Integer
Public Property TotalBarcodes As Integer
Public Property PageResults As List(Of PageBarcodeResult)
End Class
Public Class PageBarcodeResult
Public Property PageNumber As Integer
Public Property BarcodeCount As Integer
Public Property Barcodes As List(Of BarcodeData)
End Class
Esta implementación completa aborda escenarios empresariales del mundo real. El punto final de escaneo de URL incluye validación de dominio para evitar ataques de falsificación de solicitud del lado del servidor (SSRF). El controlador Base64 elimina los prefijos de URL de datos, algo común cuando se reciben imágenes de aplicaciones web.
El punto final del código de barras dañado demuestra las capacidades de corrección de imágenes de IronBarcode. Aplicando múltiples filtros y probando diferentes enfoques, puede recuperar datos de imágenes severamente degradadas. Esto resulta invaluable cuando se trata de etiquetas de inventario antiguas o documentos de envío dañados por el clima.
Cómo elegir entre escaneo de PDF y de imágenes
Los archivos PDF presentan desafíos y oportunidades únicos en el escaneo de códigos de barras empresariales. Muchos documentos comerciales, como facturas, manifiestos de envío y formularios de cumplimiento, llegan en formato PDF con códigos de barras integrados. Las capacidades de lectura de códigos de barras PDF en IronBarcode manejan estos escenarios con elegancia.
Consideremos un escenario empresarial típico: su departamento de cuentas a pagar recibe miles de facturas mensualmente, cada una de las cuales contiene códigos de barras para su procesamiento automatizado. Algunos proveedores envían archivos PDF de alta calidad, otros envían documentos escaneados con calidad variable.
El punto final de escaneo PDF avanzado maneja ambos escenarios. Para archivos PDF de alta calidad, las configuraciones estándar funcionan perfectamente. Para los documentos escaneados, aumentar los parámetros de DPI y escala mejora las tasas de detección. El procesamiento específico de página le permite extraer códigos de barras de páginas específicas, lo cual resulta útil cuando sabe que las facturas siempre tienen códigos de barras en la primera página.
Los resultados agrupados por página proporcionan un contexto valioso. Su sistema de flujo de trabajo puede enrutar documentos en función de qué páginas contienen códigos de barras, automatizando la clasificación de documentos. Este enfoque estructurado para el procesamiento de documentos de varias páginas transforma los procesos manuales en flujos de trabajo automatizados y eficientes.
Manejo eficiente de múltiples códigos de barras
Los documentos empresariales a menudo contienen múltiples códigos de barras que cumplen distintas funciones. Una etiqueta de envío puede tener un código de barras de seguimiento, un código de barras de producto y un código de barras de destino. El manejo adecuado de múltiples escenarios de códigos de barras requiere un diseño de API cuidadoso.
El punto final de procesamiento por lotes demuestra cómo manejar escenarios de gran volumen de manera eficiente. Al procesar varios archivos en paralelo, se puede reducir drásticamente el tiempo total de procesamiento. El patrón Task.WhenAll garantiza un uso óptimo de los recursos, manteniendo la estabilidad del sistema.
Para crear códigos de barras en sus aplicaciones, IronBarcode ofrece capacidades igualmente efectivas. Ya sea que necesite generar códigos de barras lineales 1D para sistemas de inventario o crear códigos matriciales 2D para aplicaciones móviles, se aplica el mismo enfoque API simple.
¿Cómo mejorar el rendimiento y la precisión?
La optimización del rendimiento en el escaneo de códigos de barras empresariales no solo se trata de velocidad: se trata de encontrar el equilibrio adecuado para su caso de uso específico. Los siguientes ejemplos exploran técnicas de optimización avanzadas que pueden transformar el rendimiento de su escaneo:
public class OptimizedScannerService
{
private readonly ILogger<OptimizedScannerService> _logger;
private readonly IMemoryCache _cache;
public OptimizedScannerService(ILogger<OptimizedScannerService> logger, IMemoryCache cache)
{
_logger = logger;
_cache = cache;
}
// Performance-optimized scanning with caching
public async Task<ScanResult> ScanWithOptimizationsAsync(byte[] imageData, string cacheKey = null)
{
// Check cache for repeat scans
if (!string.IsNullOrEmpty(cacheKey))
{
if (_cache.TryGetValue(cacheKey, out ScanResult cachedResult))
{
_logger.LogInformation($"Cache hit for {cacheKey}");
cachedResult.Metadata["CacheHit"] = true;
return cachedResult;
}
}
// Determine optimal settings based on image characteristics
var settings = await DetermineOptimalSettingsAsync(imageData);
// Apply region-specific scanning if applicable
if (settings.UseRegionScanning)
{
return await ScanWithRegionsAsync(imageData, settings);
}
// Standard optimized scanning
var results = await BarcodeReader.ReadAsync(imageData, settings.Options);
var scanResult = BuildScanResult(results);
// Cache successful results
if (!string.IsNullOrEmpty(cacheKey) && scanResult.Success)
{
_cache.Set(cacheKey, scanResult, TimeSpan.FromMinutes(5));
}
return scanResult;
}
// Intelligent settings determination
private async Task<ScanSettings> DetermineOptimalSettingsAsync(byte[] imageData)
{
var settings = new ScanSettings();
// Quick analysis of image properties
using var ms = new MemoryStream(imageData);
using var image = Image.FromStream(ms);
var width = image.Width;
var height = image.Height;
var aspectRatio = (double)width / height;
_logger.LogInformation($"Image analysis: {width}x{height}, ratio: {aspectRatio:F2}");
// High-resolution images can use faster scanning
if (width > 2000 && height > 2000)
{
settings.Options.Speed = ReadingSpeed.Faster;
settings.Options.ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128;
}
// Low-resolution needs more processing
else if (width < 800 || height < 800)
{
settings.Options.Speed = ReadingSpeed.ExtremeDetail;
settings.Options.ImageFilters = new ImageFilterCollection
{
new SharpenFilter(3),
new ContrastFilter(2)
};
}
else
{
settings.Options.Speed = ReadingSpeed.Balanced;
}
// Detect if image might contain multiple barcodes based on aspect ratio
if (aspectRatio > 2 || aspectRatio < 0.5)
{
settings.Options.ExpectMultipleBarcodes = true;
settings.UseRegionScanning = true;
}
// Enable rotation correction for potentially skewed images
settings.Options.AutoRotate = true;
return settings;
}
// Region-based scanning for large images
private async Task<ScanResult> ScanWithRegionsAsync(byte[] imageData, ScanSettings settings)
{
var allResults = new List<BarcodeResult>();
using var ms = new MemoryStream(imageData);
using var image = Image.FromStream(ms);
// Define scanning regions for common document layouts
var regions = new[]
{
new Rectangle(0, 0, image.Width / 2, image.Height / 2), // Top-left
new Rectangle(image.Width / 2, 0, image.Width / 2, image.Height / 2), // Top-right
new Rectangle(0, image.Height / 2, image.Width / 2, image.Height / 2), // Bottom-left
new Rectangle(image.Width / 2, image.Height / 2, image.Width / 2, image.Height / 2), // Bottom-right
new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2) // Center
};
var tasks = regions.Select(async region =>
{
var regionOptions = new BarcodeReaderOptions
{
CropArea = region,
Speed = ReadingSpeed.Faster,
ExpectMultipleBarcodes = false
};
try
{
return await BarcodeReader.ReadAsync(imageData, regionOptions);
}
catch (Exception ex)
{
_logger.LogWarning($"Region scan failed: {ex.Message}");
return new BarcodeResult[0];
}
});
var regionResults = await Task.WhenAll(tasks);
// Combine and deduplicate results
var uniqueResults = regionResults
.SelectMany(r => r)
.GroupBy(r => r.Value)
.Select(g => g.OrderByDescending(r => r.Confidence).First())
.ToList();
return BuildScanResult(uniqueResults);
}
// Adaptive quality enhancement
public async Task<ScanResult> ScanWithAdaptiveEnhancementAsync(byte[] imageData)
{
var attempts = new List<Func<Task<BarcodeResults>>>
{
// Attempt 1: Fast scan
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Faster,
RemoveFalsePositive = true
}),
// Attempt 2: Standard scan with basic filters
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
ImageFilters = new ImageFilterCollection
{
new ContrastFilter(1.5f),
new SharpenFilter(1)
},
TryInvertColor = true
}),
// Attempt 3: Detailed scan with aggressive filtering
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Detailed,
ImageFilters = new ImageFilterCollection
{
new AdaptiveThresholdFilter(11),
new DeNoise(2),
new MedianFilter(3)
},
AutoRotate = true,
Confidence = ConfidenceLevel.Low
}),
// Attempt 4: Extreme processing for damaged barcodes
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.ExtremeDetail,
ImageFilters = new ImageFilterCollection
{
new BinaryThresholdFilter(100),
new Erode(1),
new Dilate(2),
new SharpenFilter(4)
},
RemoveFalsePositive = false,
UseCode39ExtendedMode = true,
TryInvertColor = true
})
};
foreach (var (attempt, index) in attempts.Select((a, i) => (a, i)))
{
var stopwatch = Stopwatch.StartNew();
var results = await attempt();
stopwatch.Stop();
_logger.LogInformation($"Attempt {index + 1} took {stopwatch.ElapsedMilliseconds}ms, found {results.Count()} barcodes");
if (results.Any())
{
var scanResult = BuildScanResult(results);
scanResult.Metadata["AttemptNumber"] = index + 1;
scanResult.Metadata["ProcessingStrategy"] = GetStrategyName(index);
return scanResult;
}
}
return new ScanResult
{
Success = false,
ErrorMessage = "No barcodes found after all enhancement attempts"
};
}
// Machine learning confidence optimization
public class MLOptimizedScanner
{
private readonly Dictionary<string, double> _formatConfidenceThresholds = new()
{
{ "QRCode", 85.0 },
{ "Code128", 90.0 },
{ "Code39", 88.0 },
{ "DataMatrix", 87.0 },
{ "EAN13", 92.0 },
{ "PDF417", 86.0 }
};
public async Task<ScanResult> ScanWithMLConfidenceAsync(byte[] imageData, string expectedFormat = null)
{
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
Confidence = ConfidenceLevel.Optional
};
// If we know the expected format, improve for it
if (!string.IsNullOrEmpty(expectedFormat) &&
Enum.TryParse<BarcodeEncoding>(expectedFormat, out var encoding))
{
options.ExpectBarcodeTypes = encoding;
}
var results = await BarcodeReader.ReadAsync(imageData, options);
// Apply ML confidence filtering
var confidenceThreshold = expectedFormat != null &&
_formatConfidenceThresholds.ContainsKey(expectedFormat)
? _formatConfidenceThresholds[expectedFormat]
: 80.0;
var highConfidenceResults = results
.Where(r => r.Confidence >= confidenceThreshold)
.ToList();
if (highConfidenceResults.Any())
{
return BuildScanResult(highConfidenceResults);
}
// If no high-confidence results, include lower confidence with warnings
var lowConfidenceResults = results
.Where(r => r.Confidence < confidenceThreshold)
.ToList();
if (lowConfidenceResults.Any())
{
var result = BuildScanResult(lowConfidenceResults);
result.Metadata["Warning"] = "Low confidence results - manual verification recommended";
result.Metadata["MinConfidence"] = lowConfidenceResults.Min(r => r.Confidence);
return result;
}
return new ScanResult
{
Success = false,
ErrorMessage = "No barcodes met confidence threshold"
};
}
}
private ScanResult BuildScanResult(IEnumerable<BarcodeResult> results)
{
var resultList = results.ToList();
return new ScanResult
{
Success = true,
Barcodes = resultList.Select(r => new BarcodeData
{
Value = r.Value,
Format = r.BarcodeType.ToString(),
Confidence = r.Confidence,
Location = r.Bounds
}).ToList(),
Metadata = new Dictionary<string, object>
{
["TotalFound"] = resultList.Count,
["AverageConfidence"] = resultList.Average(r => r.Confidence),
["Formats"] = resultList.Select(r => r.BarcodeType.ToString()).Distinct().ToArray()
}
};
}
private string GetStrategyName(int attemptIndex)
{
return attemptIndex switch
{
0 => "FastScan",
1 => "StandardEnhanced",
2 => "DetailedFiltering",
3 => "ExtremeDamageRecovery",
_ => "Unknown"
};
}
}
// Supporting classes
public class ScanSettings
{
public BarcodeReaderOptions Options { get; set; } = new BarcodeReaderOptions();
public bool UseRegionScanning { get; set; }
public List<Rectangle> CustomRegions { get; set; }
}
// Benchmark service for performance testing
public class BenchmarkService
{
private readonly ILogger<BenchmarkService> _logger;
public async Task<BenchmarkResult> BenchmarkSettingsAsync(byte[] testImage, int iterations = 10)
{
var results = new Dictionary<string, BenchmarkData>();
var testConfigurations = new Dictionary<string, BarcodeReaderOptions>
{
["Fastest"] = new() { Speed = ReadingSpeed.Faster },
["Balanced"] = new() { Speed = ReadingSpeed.Balanced },
["Detailed"] = new() { Speed = ReadingSpeed.Detailed },
["Parallel"] = new() { Speed = ReadingSpeed.Balanced, Multithreaded = true, MaxParallelThreads = 4 },
["Filtered"] = new()
{
Speed = ReadingSpeed.Balanced,
ImageFilters = new ImageFilterCollection { new SharpenFilter(2), new ContrastFilter(1.5f) }
}
};
foreach (var config in testConfigurations)
{
var times = new List<long>();
var successCount = 0;
for (int i = 0; i < iterations; i++)
{
var sw = Stopwatch.StartNew();
var scanResults = await BarcodeReader.ReadAsync(testImage, config.Value);
sw.Stop();
times.Add(sw.ElapsedMilliseconds);
if (scanResults.Any()) successCount++;
// Small delay between tests
await Task.Delay(100);
}
results[config.Key] = new BenchmarkData
{
AverageMs = times.Average(),
MinMs = times.Min(),
MaxMs = times.Max(),
SuccessRate = (double)successCount / iterations * 100
};
}
return new BenchmarkResult { Results = results };
}
}
public class BenchmarkData
{
public double AverageMs { get; set; }
public long MinMs { get; set; }
public long MaxMs { get; set; }
public double SuccessRate { get; set; }
}
public class BenchmarkResult
{
public Dictionary<string, BenchmarkData> Results { get; set; }
}
public class OptimizedScannerService
{
private readonly ILogger<OptimizedScannerService> _logger;
private readonly IMemoryCache _cache;
public OptimizedScannerService(ILogger<OptimizedScannerService> logger, IMemoryCache cache)
{
_logger = logger;
_cache = cache;
}
// Performance-optimized scanning with caching
public async Task<ScanResult> ScanWithOptimizationsAsync(byte[] imageData, string cacheKey = null)
{
// Check cache for repeat scans
if (!string.IsNullOrEmpty(cacheKey))
{
if (_cache.TryGetValue(cacheKey, out ScanResult cachedResult))
{
_logger.LogInformation($"Cache hit for {cacheKey}");
cachedResult.Metadata["CacheHit"] = true;
return cachedResult;
}
}
// Determine optimal settings based on image characteristics
var settings = await DetermineOptimalSettingsAsync(imageData);
// Apply region-specific scanning if applicable
if (settings.UseRegionScanning)
{
return await ScanWithRegionsAsync(imageData, settings);
}
// Standard optimized scanning
var results = await BarcodeReader.ReadAsync(imageData, settings.Options);
var scanResult = BuildScanResult(results);
// Cache successful results
if (!string.IsNullOrEmpty(cacheKey) && scanResult.Success)
{
_cache.Set(cacheKey, scanResult, TimeSpan.FromMinutes(5));
}
return scanResult;
}
// Intelligent settings determination
private async Task<ScanSettings> DetermineOptimalSettingsAsync(byte[] imageData)
{
var settings = new ScanSettings();
// Quick analysis of image properties
using var ms = new MemoryStream(imageData);
using var image = Image.FromStream(ms);
var width = image.Width;
var height = image.Height;
var aspectRatio = (double)width / height;
_logger.LogInformation($"Image analysis: {width}x{height}, ratio: {aspectRatio:F2}");
// High-resolution images can use faster scanning
if (width > 2000 && height > 2000)
{
settings.Options.Speed = ReadingSpeed.Faster;
settings.Options.ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128;
}
// Low-resolution needs more processing
else if (width < 800 || height < 800)
{
settings.Options.Speed = ReadingSpeed.ExtremeDetail;
settings.Options.ImageFilters = new ImageFilterCollection
{
new SharpenFilter(3),
new ContrastFilter(2)
};
}
else
{
settings.Options.Speed = ReadingSpeed.Balanced;
}
// Detect if image might contain multiple barcodes based on aspect ratio
if (aspectRatio > 2 || aspectRatio < 0.5)
{
settings.Options.ExpectMultipleBarcodes = true;
settings.UseRegionScanning = true;
}
// Enable rotation correction for potentially skewed images
settings.Options.AutoRotate = true;
return settings;
}
// Region-based scanning for large images
private async Task<ScanResult> ScanWithRegionsAsync(byte[] imageData, ScanSettings settings)
{
var allResults = new List<BarcodeResult>();
using var ms = new MemoryStream(imageData);
using var image = Image.FromStream(ms);
// Define scanning regions for common document layouts
var regions = new[]
{
new Rectangle(0, 0, image.Width / 2, image.Height / 2), // Top-left
new Rectangle(image.Width / 2, 0, image.Width / 2, image.Height / 2), // Top-right
new Rectangle(0, image.Height / 2, image.Width / 2, image.Height / 2), // Bottom-left
new Rectangle(image.Width / 2, image.Height / 2, image.Width / 2, image.Height / 2), // Bottom-right
new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2) // Center
};
var tasks = regions.Select(async region =>
{
var regionOptions = new BarcodeReaderOptions
{
CropArea = region,
Speed = ReadingSpeed.Faster,
ExpectMultipleBarcodes = false
};
try
{
return await BarcodeReader.ReadAsync(imageData, regionOptions);
}
catch (Exception ex)
{
_logger.LogWarning($"Region scan failed: {ex.Message}");
return new BarcodeResult[0];
}
});
var regionResults = await Task.WhenAll(tasks);
// Combine and deduplicate results
var uniqueResults = regionResults
.SelectMany(r => r)
.GroupBy(r => r.Value)
.Select(g => g.OrderByDescending(r => r.Confidence).First())
.ToList();
return BuildScanResult(uniqueResults);
}
// Adaptive quality enhancement
public async Task<ScanResult> ScanWithAdaptiveEnhancementAsync(byte[] imageData)
{
var attempts = new List<Func<Task<BarcodeResults>>>
{
// Attempt 1: Fast scan
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Faster,
RemoveFalsePositive = true
}),
// Attempt 2: Standard scan with basic filters
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
ImageFilters = new ImageFilterCollection
{
new ContrastFilter(1.5f),
new SharpenFilter(1)
},
TryInvertColor = true
}),
// Attempt 3: Detailed scan with aggressive filtering
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.Detailed,
ImageFilters = new ImageFilterCollection
{
new AdaptiveThresholdFilter(11),
new DeNoise(2),
new MedianFilter(3)
},
AutoRotate = true,
Confidence = ConfidenceLevel.Low
}),
// Attempt 4: Extreme processing for damaged barcodes
async () => await BarcodeReader.ReadAsync(imageData, new BarcodeReaderOptions
{
Speed = ReadingSpeed.ExtremeDetail,
ImageFilters = new ImageFilterCollection
{
new BinaryThresholdFilter(100),
new Erode(1),
new Dilate(2),
new SharpenFilter(4)
},
RemoveFalsePositive = false,
UseCode39ExtendedMode = true,
TryInvertColor = true
})
};
foreach (var (attempt, index) in attempts.Select((a, i) => (a, i)))
{
var stopwatch = Stopwatch.StartNew();
var results = await attempt();
stopwatch.Stop();
_logger.LogInformation($"Attempt {index + 1} took {stopwatch.ElapsedMilliseconds}ms, found {results.Count()} barcodes");
if (results.Any())
{
var scanResult = BuildScanResult(results);
scanResult.Metadata["AttemptNumber"] = index + 1;
scanResult.Metadata["ProcessingStrategy"] = GetStrategyName(index);
return scanResult;
}
}
return new ScanResult
{
Success = false,
ErrorMessage = "No barcodes found after all enhancement attempts"
};
}
// Machine learning confidence optimization
public class MLOptimizedScanner
{
private readonly Dictionary<string, double> _formatConfidenceThresholds = new()
{
{ "QRCode", 85.0 },
{ "Code128", 90.0 },
{ "Code39", 88.0 },
{ "DataMatrix", 87.0 },
{ "EAN13", 92.0 },
{ "PDF417", 86.0 }
};
public async Task<ScanResult> ScanWithMLConfidenceAsync(byte[] imageData, string expectedFormat = null)
{
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
Confidence = ConfidenceLevel.Optional
};
// If we know the expected format, improve for it
if (!string.IsNullOrEmpty(expectedFormat) &&
Enum.TryParse<BarcodeEncoding>(expectedFormat, out var encoding))
{
options.ExpectBarcodeTypes = encoding;
}
var results = await BarcodeReader.ReadAsync(imageData, options);
// Apply ML confidence filtering
var confidenceThreshold = expectedFormat != null &&
_formatConfidenceThresholds.ContainsKey(expectedFormat)
? _formatConfidenceThresholds[expectedFormat]
: 80.0;
var highConfidenceResults = results
.Where(r => r.Confidence >= confidenceThreshold)
.ToList();
if (highConfidenceResults.Any())
{
return BuildScanResult(highConfidenceResults);
}
// If no high-confidence results, include lower confidence with warnings
var lowConfidenceResults = results
.Where(r => r.Confidence < confidenceThreshold)
.ToList();
if (lowConfidenceResults.Any())
{
var result = BuildScanResult(lowConfidenceResults);
result.Metadata["Warning"] = "Low confidence results - manual verification recommended";
result.Metadata["MinConfidence"] = lowConfidenceResults.Min(r => r.Confidence);
return result;
}
return new ScanResult
{
Success = false,
ErrorMessage = "No barcodes met confidence threshold"
};
}
}
private ScanResult BuildScanResult(IEnumerable<BarcodeResult> results)
{
var resultList = results.ToList();
return new ScanResult
{
Success = true,
Barcodes = resultList.Select(r => new BarcodeData
{
Value = r.Value,
Format = r.BarcodeType.ToString(),
Confidence = r.Confidence,
Location = r.Bounds
}).ToList(),
Metadata = new Dictionary<string, object>
{
["TotalFound"] = resultList.Count,
["AverageConfidence"] = resultList.Average(r => r.Confidence),
["Formats"] = resultList.Select(r => r.BarcodeType.ToString()).Distinct().ToArray()
}
};
}
private string GetStrategyName(int attemptIndex)
{
return attemptIndex switch
{
0 => "FastScan",
1 => "StandardEnhanced",
2 => "DetailedFiltering",
3 => "ExtremeDamageRecovery",
_ => "Unknown"
};
}
}
// Supporting classes
public class ScanSettings
{
public BarcodeReaderOptions Options { get; set; } = new BarcodeReaderOptions();
public bool UseRegionScanning { get; set; }
public List<Rectangle> CustomRegions { get; set; }
}
// Benchmark service for performance testing
public class BenchmarkService
{
private readonly ILogger<BenchmarkService> _logger;
public async Task<BenchmarkResult> BenchmarkSettingsAsync(byte[] testImage, int iterations = 10)
{
var results = new Dictionary<string, BenchmarkData>();
var testConfigurations = new Dictionary<string, BarcodeReaderOptions>
{
["Fastest"] = new() { Speed = ReadingSpeed.Faster },
["Balanced"] = new() { Speed = ReadingSpeed.Balanced },
["Detailed"] = new() { Speed = ReadingSpeed.Detailed },
["Parallel"] = new() { Speed = ReadingSpeed.Balanced, Multithreaded = true, MaxParallelThreads = 4 },
["Filtered"] = new()
{
Speed = ReadingSpeed.Balanced,
ImageFilters = new ImageFilterCollection { new SharpenFilter(2), new ContrastFilter(1.5f) }
}
};
foreach (var config in testConfigurations)
{
var times = new List<long>();
var successCount = 0;
for (int i = 0; i < iterations; i++)
{
var sw = Stopwatch.StartNew();
var scanResults = await BarcodeReader.ReadAsync(testImage, config.Value);
sw.Stop();
times.Add(sw.ElapsedMilliseconds);
if (scanResults.Any()) successCount++;
// Small delay between tests
await Task.Delay(100);
}
results[config.Key] = new BenchmarkData
{
AverageMs = times.Average(),
MinMs = times.Min(),
MaxMs = times.Max(),
SuccessRate = (double)successCount / iterations * 100
};
}
return new BenchmarkResult { Results = results };
}
}
public class BenchmarkData
{
public double AverageMs { get; set; }
public long MinMs { get; set; }
public long MaxMs { get; set; }
public double SuccessRate { get; set; }
}
public class BenchmarkResult
{
public Dictionary<string, BenchmarkData> Results { get; set; }
}
Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks
Public Class OptimizedScannerService
Private ReadOnly _logger As ILogger(Of OptimizedScannerService)
Private ReadOnly _cache As IMemoryCache
Public Sub New(logger As ILogger(Of OptimizedScannerService), cache As IMemoryCache)
_logger = logger
_cache = cache
End Sub
' Performance-optimized scanning with caching
Public Async Function ScanWithOptimizationsAsync(imageData As Byte(), Optional cacheKey As String = Nothing) As Task(Of ScanResult)
' Check cache for repeat scans
If Not String.IsNullOrEmpty(cacheKey) Then
Dim cachedResult As ScanResult = Nothing
If _cache.TryGetValue(cacheKey, cachedResult) Then
_logger.LogInformation($"Cache hit for {cacheKey}")
cachedResult.Metadata("CacheHit") = True
Return cachedResult
End If
End If
' Determine optimal settings based on image characteristics
Dim settings = Await DetermineOptimalSettingsAsync(imageData)
' Apply region-specific scanning if applicable
If settings.UseRegionScanning Then
Return Await ScanWithRegionsAsync(imageData, settings)
End If
' Standard optimized scanning
Dim results = Await BarcodeReader.ReadAsync(imageData, settings.Options)
Dim scanResult = BuildScanResult(results)
' Cache successful results
If Not String.IsNullOrEmpty(cacheKey) AndAlso scanResult.Success Then
_cache.Set(cacheKey, scanResult, TimeSpan.FromMinutes(5))
End If
Return scanResult
End Function
' Intelligent settings determination
Private Async Function DetermineOptimalSettingsAsync(imageData As Byte()) As Task(Of ScanSettings)
Dim settings As New ScanSettings()
' Quick analysis of image properties
Using ms As New MemoryStream(imageData)
Using image As Image = Image.FromStream(ms)
Dim width = image.Width
Dim height = image.Height
Dim aspectRatio = CDbl(width) / height
_logger.LogInformation($"Image analysis: {width}x{height}, ratio: {aspectRatio:F2}")
' High-resolution images can use faster scanning
If width > 2000 AndAlso height > 2000 Then
settings.Options.Speed = ReadingSpeed.Faster
settings.Options.ExpectBarcodeTypes = BarcodeEncoding.QRCode Or BarcodeEncoding.Code128
ElseIf width < 800 OrElse height < 800 Then
' Low-resolution needs more processing
settings.Options.Speed = ReadingSpeed.ExtremeDetail
settings.Options.ImageFilters = New ImageFilterCollection From {
New SharpenFilter(3),
New ContrastFilter(2)
}
Else
settings.Options.Speed = ReadingSpeed.Balanced
End If
' Detect if image might contain multiple barcodes based on aspect ratio
If aspectRatio > 2 OrElse aspectRatio < 0.5 Then
settings.Options.ExpectMultipleBarcodes = True
settings.UseRegionScanning = True
End If
' Enable rotation correction for potentially skewed images
settings.Options.AutoRotate = True
End Using
End Using
Return settings
End Function
' Region-based scanning for large images
Private Async Function ScanWithRegionsAsync(imageData As Byte(), settings As ScanSettings) As Task(Of ScanResult)
Dim allResults As New List(Of BarcodeResult)()
Using ms As New MemoryStream(imageData)
Using image As Image = Image.FromStream(ms)
' Define scanning regions for common document layouts
Dim regions = {
New Rectangle(0, 0, image.Width \ 2, image.Height \ 2), ' Top-left
New Rectangle(image.Width \ 2, 0, image.Width \ 2, image.Height \ 2), ' Top-right
New Rectangle(0, image.Height \ 2, image.Width \ 2, image.Height \ 2), ' Bottom-left
New Rectangle(image.Width \ 2, image.Height \ 2, image.Width \ 2, image.Height \ 2), ' Bottom-right
New Rectangle(image.Width \ 4, image.Height \ 4, image.Width \ 2, image.Height \ 2) ' Center
}
Dim tasks = regions.Select(Async Function(region)
Dim regionOptions As New BarcodeReaderOptions With {
.CropArea = region,
.Speed = ReadingSpeed.Faster,
.ExpectMultipleBarcodes = False
}
Try
Return Await BarcodeReader.ReadAsync(imageData, regionOptions)
Catch ex As Exception
_logger.LogWarning($"Region scan failed: {ex.Message}")
Return New BarcodeResult() {}
End Try
End Function)
Dim regionResults = Await Task.WhenAll(tasks)
' Combine and deduplicate results
Dim uniqueResults = regionResults.
SelectMany(Function(r) r).
GroupBy(Function(r) r.Value).
Select(Function(g) g.OrderByDescending(Function(r) r.Confidence).First()).
ToList()
Return BuildScanResult(uniqueResults)
End Using
End Using
End Function
' Adaptive quality enhancement
Public Async Function ScanWithAdaptiveEnhancementAsync(imageData As Byte()) As Task(Of ScanResult)
Dim attempts = New List(Of Func(Of Task(Of BarcodeResults))) From {
' Attempt 1: Fast scan
Async Function() Await BarcodeReader.ReadAsync(imageData, New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Faster,
.RemoveFalsePositive = True
}),
' Attempt 2: Standard scan with basic filters
Async Function() Await BarcodeReader.ReadAsync(imageData, New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Balanced,
.ImageFilters = New ImageFilterCollection From {
New ContrastFilter(1.5F),
New SharpenFilter(1)
},
.TryInvertColor = True
}),
' Attempt 3: Detailed scan with aggressive filtering
Async Function() Await BarcodeReader.ReadAsync(imageData, New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Detailed,
.ImageFilters = New ImageFilterCollection From {
New AdaptiveThresholdFilter(11),
New DeNoise(2),
New MedianFilter(3)
},
.AutoRotate = True,
.Confidence = ConfidenceLevel.Low
}),
' Attempt 4: Extreme processing for damaged barcodes
Async Function() Await BarcodeReader.ReadAsync(imageData, New BarcodeReaderOptions With {
.Speed = ReadingSpeed.ExtremeDetail,
.ImageFilters = New ImageFilterCollection From {
New BinaryThresholdFilter(100),
New Erode(1),
New Dilate(2),
New SharpenFilter(4)
},
.RemoveFalsePositive = False,
.UseCode39ExtendedMode = True,
.TryInvertColor = True
})
}
For Each attempt In attempts.Select(Function(a, i) (a, i))
Dim stopwatch = Stopwatch.StartNew()
Dim results = Await attempt.a()
stopwatch.Stop()
_logger.LogInformation($"Attempt {attempt.i + 1} took {stopwatch.ElapsedMilliseconds}ms, found {results.Count()} barcodes")
If results.Any() Then
Dim scanResult = BuildScanResult(results)
scanResult.Metadata("AttemptNumber") = attempt.i + 1
scanResult.Metadata("ProcessingStrategy") = GetStrategyName(attempt.i)
Return scanResult
End If
Next
Return New ScanResult With {
.Success = False,
.ErrorMessage = "No barcodes found after all enhancement attempts"
}
End Function
' Machine learning confidence optimization
Public Class MLOptimizedScanner
Private ReadOnly _formatConfidenceThresholds As New Dictionary(Of String, Double) From {
{"QRCode", 85.0},
{"Code128", 90.0},
{"Code39", 88.0},
{"DataMatrix", 87.0},
{"EAN13", 92.0},
{"PDF417", 86.0}
}
Public Async Function ScanWithMLConfidenceAsync(imageData As Byte(), Optional expectedFormat As String = Nothing) As Task(Of ScanResult)
Dim options As New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Balanced,
.Confidence = ConfidenceLevel.Optional
}
' If we know the expected format, improve for it
If Not String.IsNullOrEmpty(expectedFormat) AndAlso
[Enum].TryParse(Of BarcodeEncoding)(expectedFormat, encoding) Then
options.ExpectBarcodeTypes = encoding
End If
Dim results = Await BarcodeReader.ReadAsync(imageData, options)
' Apply ML confidence filtering
Dim confidenceThreshold = If(expectedFormat IsNot Nothing AndAlso
_formatConfidenceThresholds.ContainsKey(expectedFormat),
_formatConfidenceThresholds(expectedFormat),
80.0)
Dim highConfidenceResults = results.
Where(Function(r) r.Confidence >= confidenceThreshold).
ToList()
If highConfidenceResults.Any() Then
Return BuildScanResult(highConfidenceResults)
End If
' If no high-confidence results, include lower confidence with warnings
Dim lowConfidenceResults = results.
Where(Function(r) r.Confidence < confidenceThreshold).
ToList()
If lowConfidenceResults.Any() Then
Dim result = BuildScanResult(lowConfidenceResults)
result.Metadata("Warning") = "Low confidence results - manual verification recommended"
result.Metadata("MinConfidence") = lowConfidenceResults.Min(Function(r) r.Confidence)
Return result
End If
Return New ScanResult With {
.Success = False,
.ErrorMessage = "No barcodes met confidence threshold"
}
End Function
End Class
Private Function BuildScanResult(results As IEnumerable(Of BarcodeResult)) As ScanResult
Dim resultList = results.ToList()
Return New ScanResult With {
.Success = True,
.Barcodes = resultList.Select(Function(r) New BarcodeData With {
.Value = r.Value,
.Format = r.BarcodeType.ToString(),
.Confidence = r.Confidence,
.Location = r.Bounds
}).ToList(),
.Metadata = New Dictionary(Of String, Object) From {
{"TotalFound", resultList.Count},
{"AverageConfidence", resultList.Average(Function(r) r.Confidence)},
{"Formats", resultList.Select(Function(r) r.BarcodeType.ToString()).Distinct().ToArray()}
}
}
End Function
Private Function GetStrategyName(attemptIndex As Integer) As String
Return attemptIndex Select Case {
0 : "FastScan",
1 : "StandardEnhanced",
2 : "DetailedFiltering",
3 : "ExtremeDamageRecovery",
Else : "Unknown"
}
End Function
End Class
' Supporting classes
Public Class ScanSettings
Public Property Options As BarcodeReaderOptions = New BarcodeReaderOptions()
Public Property UseRegionScanning As Boolean
Public Property CustomRegions As List(Of Rectangle)
End Class
' Benchmark service for performance testing
Public Class BenchmarkService
Private ReadOnly _logger As ILogger(Of BenchmarkService)
Public Async Function BenchmarkSettingsAsync(testImage As Byte(), Optional iterations As Integer = 10) As Task(Of BenchmarkResult)
Dim results As New Dictionary(Of String, BenchmarkData)()
Dim testConfigurations = New Dictionary(Of String, BarcodeReaderOptions) From {
{"Fastest", New BarcodeReaderOptions With {.Speed = ReadingSpeed.Faster}},
{"Balanced", New BarcodeReaderOptions With {.Speed = ReadingSpeed.Balanced}},
{"Detailed", New BarcodeReaderOptions With {.Speed = ReadingSpeed.Detailed}},
{"Parallel", New BarcodeReaderOptions With {.Speed = ReadingSpeed.Balanced, .Multithreaded = True, .MaxParallelThreads = 4}},
{"Filtered", New BarcodeReaderOptions With {.Speed = ReadingSpeed.Balanced, .ImageFilters = New ImageFilterCollection From {New SharpenFilter(2), New ContrastFilter(1.5F)}}}
}
For Each config In testConfigurations
Dim times As New List(Of Long)()
Dim successCount = 0
For i = 0 To iterations - 1
Dim sw = Stopwatch.StartNew()
Dim scanResults = Await BarcodeReader.ReadAsync(testImage, config.Value)
sw.Stop()
times.Add(sw.ElapsedMilliseconds)
If scanResults.Any() Then successCount += 1
' Small delay between tests
Await Task.Delay(100)
Next
results(config.Key) = New BenchmarkData With {
.AverageMs = times.Average(),
.MinMs = times.Min(),
.MaxMs = times.Max(),
.SuccessRate = CDbl(successCount) / iterations * 100
}
Next
Return New BenchmarkResult With {.Results = results}
End Function
End Class
Public Class BenchmarkData
Public Property AverageMs As Double
Public Property MinMs As Long
Public Property MaxMs As Long
Public Property SuccessRate As Double
End Class
Public Class BenchmarkResult
Public Property Results As Dictionary(Of String, BenchmarkData)
End Class
Este enfoque de optimización avanzado demuestra varias técnicas profesionales. La determinación de configuración inteligente analiza las propiedades de la imagen para seleccionar automáticamente los parámetros de procesamiento óptimos. Las imágenes de alta resolución de las cámaras modernas pueden utilizar un procesamiento más rápido, mientras que los escaneos de baja calidad reciben un filtrado mejorado.
El escaneo basado en regiones resulta especialmente efectivo para documentos de gran formato. Al dividir la imagen en regiones y procesarlas en paralelo, puede lograr mejoras significativas en el rendimiento. Esta técnica funciona especialmente bien para formularios estandarizados donde las ubicaciones de los códigos de barras son predecibles.
Cómo elegir la configuración correcta ReadingSpeed
El enfoque de mejora adaptativa prueba técnicas de procesamiento progresivamente más agresivas hasta tener éxito. Esto garantiza el procesamiento más rápido posible de códigos de barras de buena calidad y al mismo tiempo un manejo exitoso de los dañados. La función de umbral de confianza utiliza el aprendizaje automático para validar los resultados, lo que reduce los falsos positivos en entornos de producción.
Para escenarios de optimización específicos, la siguiente tabla asigna casos de uso comunes a configuraciones recomendadas:
| Caso de uso | Velocidad recomendada | Opciones adicionales | Compensación esperada |
|---|---|---|---|
| Procesamiento por lotes de gran volumen | `Faster` | Filtrar por tipo de código de barras esperado | Rendimiento más rápido; puede pasar por alto códigos de barras degradados |
| Documentos de calidad mixta | `Balanced` | Reserva de mejora adaptativa | Buen equilibrio entre velocidad y precisión. |
| Códigos de barras dañados o envejecidos | `ExtremeDetail` | Canalización de filtro de imagen completa | Máxima precisión; rendimiento más lento |
| Puntos finales de API en tiempo real | `Balanced` | Almacenamiento en caché + escaneo de regiones | Tiempos de respuesta consistentes inferiores a 200 ms |
| Extracción de facturas en PDF | `Balanced` | DPI 300, Escala 3 | Ideal para archivos PDF con capas de texto |
El servicio de referencia le ayuda a tomar decisiones basadas en datos sobre la configuración. Ejecute pruebas de rendimiento con sus muestras de códigos de barras reales para encontrar el equilibrio óptimo entre velocidad y precisión para su caso de uso específico. Las pautas de la industria de las mejores prácticas de rendimiento de Microsoft para ASP.NET Core también se aplican directamente al ajustar los puntos finales de la API del escáner.
Impacto de los filtros de imagen
Los filtros de imagen pueden mejorar drásticamente las tasas de reconocimiento de códigos de barras complejos. La documentación de corrección de imagen explica el propósito de cada filtro. Sin embargo, los filtros también aumentan el tiempo de procesamiento, por lo que es necesario utilizarlos con cuidado.
Para implementaciones empresariales, considere implementar un enfoque escalonado:
- Escaneo rápido sin filtros para el intento inicial
- Aplicar filtros básicos si el primer intento falla
- Utilice un filtrado agresivo solo cuando sea necesario
- Almacenar en caché los resultados para evitar el reprocesamiento
Esta estrategia mantiene tiempos de respuesta promedio rápidos y al mismo tiempo maneja casos difíciles con éxito. La función de corrección de orientación resulta especialmente valiosa para imágenes capturadas con dispositivos móviles, donde los usuarios pueden sostener los dispositivos en distintos ángulos.
¿Cómo generar imágenes de códigos QR en C#?
Si bien el escaneo es crucial, muchas aplicaciones empresariales también necesitan generar códigos de barras. IronBarcode hace que la creación sea tan sencilla como la lectura. A continuación se explica cómo generar códigos QR para diversos escenarios empresariales:
public class BarcodeGenerationService
{
private readonly ILogger<BarcodeGenerationService> _logger;
// Generate QR codes for asset tracking
public byte[] GenerateAssetQRCode(AssetInfo asset)
{
// Create JSON payload with asset information
var assetData = JsonSerializer.Serialize(new
{
Id = asset.AssetId,
Type = asset.AssetType,
Location = asset.CurrentLocation,
LastMaintenance = asset.LastMaintenanceDate,
Url = "___PROTECTED_URL_45___"
});
// Generate QR code with high error correction for durability
var qrCode = QRCodeWriter.CreateQrCode(assetData, 500, QRCodeWriter.QrErrorCorrectionLevel.High);
// Add company branding
qrCode.AddAnnotationTextAboveBarcode($"ASSET: {asset.AssetId}");
qrCode.AddAnnotationTextBelowBarcode(asset.AssetType);
qrCode.SetMargins(10);
// Style for printing on asset labels
qrCode.ChangeBarCodeColor(Color.Black);
qrCode.ChangeBackgroundColor(Color.White);
return qrCode.ToStream().ToArray();
}
// Generate visitor badges with QR codes
public GeneratedBarcode CreateVisitorBadgeQR(VisitorInfo visitor)
{
// Encode visitor information with expiration
var visitorData = new
{
Name = visitor.Name,
Company = visitor.Company,
Host = visitor.HostEmployee,
ValidFrom = visitor.CheckInTime,
ValidUntil = visitor.CheckInTime.AddHours(8),
AccessLevel = visitor.AccessLevel,
BadgeId = Guid.NewGuid().ToString()
};
var qrCode = QRCodeWriter.CreateQrCode(
JsonSerializer.Serialize(visitorData),
400,
QRCodeWriter.QrErrorCorrectionLevel.Medium
);
// Add company logo for professional appearance
if (File.Exists("company-logo.png"))
{
qrCode.AddLogo("company-logo.png");
}
// Style for badge printing
qrCode.SetMargins(15);
qrCode.ChangeBarCodeColor(Color.FromArgb(0, 48, 135)); // Company blue
// Add visible text for security personnel
qrCode.AddAnnotationTextAboveBarcode($"VISITOR: {visitor.Name}");
qrCode.AddAnnotationTextBelowBarcode($"Expires: {visitor.CheckInTime.AddHours(8):HH:mm}");
_logger.LogInformation($"Generated visitor badge for {visitor.Name}, BadgeId: {visitorData.BadgeId}");
return qrCode;
}
// Generate shipping labels with multiple barcodes
public byte[] GenerateShippingLabel(ShippingInfo shipping)
{
// Create a PDF with multiple barcodes
var pdf = new IronPdf.ChromePdfRenderer();
// Generate tracking barcode (Code 128 for USPS/UPS compatibility)
var trackingBarcode = BarcodeWriter.CreateBarcode(
shipping.TrackingNumber,
BarcodeEncoding.Code128
);
trackingBarcode.ResizeTo(300, 75);
trackingBarcode.SetMargins(5);
// Generate postal code barcode
var postalBarcode = BarcodeWriter.CreateBarcode(
shipping.PostalCode,
BarcodeEncoding.Code128
);
postalBarcode.ResizeTo(200, 50);
// Generate QR code with complete shipping data
var shippingData = JsonSerializer.Serialize(shipping);
var qrCode = QRCodeWriter.CreateQrCode(shippingData, 200);
// Combine into shipping label HTML
var labelHtml = $@
<html>
<body style='font-family: Arial, sans-serif;'>
<div style='border: 2px solid black; padding: 20px; width: 4in; height: 6in;'>
<h2>SHIPPING LABEL</h2>
<hr/>
<p><strong>From:</strong><br/>{shipping.SenderAddress}</p>
<p><strong>To:</strong><br/>{shipping.RecipientAddress}</p>
<hr/>
<div style='text-align: center;'>
<img src='{trackingBarcode.ToDataUrl()}' alt='Tracking barcode'/>
<p>Tracking: {shipping.TrackingNumber}</p>
</div>
<div style='margin-top: 20px;'>
<img src='{postalBarcode.ToDataUrl()}' alt='Postal barcode' style='float: left;'/>
<img src='{qrCode.ToDataUrl()}' alt='Shipping QR code' style='float: right; width: 100px;'/>
</div>
<div style='clear: both; margin-top: 20px; font-size: 10px;'>
<p>Service: {shipping.ServiceType} | Weight: {shipping.Weight}lbs</p>
</div>
</div>
</body>
</html>";
return pdf.RenderHtmlAsPdf(labelHtml).BinaryData;
}
// Generate secure document QR codes with encryption
public class SecureDocumentQR
{
private readonly byte[] _encryptionKey;
public SecureDocumentQR(byte[] encryptionKey)
{
_encryptionKey = encryptionKey;
}
public GeneratedBarcode GenerateSecureDocumentQR(DocumentInfo document)
{
// Create document reference with security features
var documentRef = new
{
DocumentId = document.Id,
Type = document.DocumentType,
CreatedDate = document.CreatedDate,
Hash = ComputeDocumentHash(document),
AccessUrl = "___PROTECTED_URL_46___",
ValidUntil = DateTime.UtcNow.AddDays(30)
};
// Encrypt sensitive data
var jsonData = JsonSerializer.Serialize(documentRef);
var encryptedData = EncryptData(jsonData);
// Generate QR with encrypted payload
var qrCode = QRCodeWriter.CreateQrCode(
Convert.ToBase64String(encryptedData),
500,
QRCodeWriter.QrErrorCorrectionLevel.High
);
// Add visual security indicators
qrCode.ChangeBarCodeColor(Color.DarkGreen);
qrCode.AddAnnotationTextAboveBarcode("SECURE DOCUMENT");
qrCode.AddAnnotationTextBelowBarcode($"Expires: {documentRef.ValidUntil:yyyy-MM-dd}");
return qrCode;
}
private string ComputeDocumentHash(DocumentInfo document)
{
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(document.Content ?? document.Id));
return BitConverter.ToString(hash).Replace("-", "");
}
private byte[] EncryptData(string data)
{
// Simplified encryption - use proper encryption in production
using var aes = Aes.Create();
aes.Key = _encryptionKey;
aes.GenerateIV();
using var encryptor = aes.CreateEncryptor();
var dataBytes = Encoding.UTF8.GetBytes(data);
var encrypted = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length);
// Prepend IV for decryption
return aes.IV.Concat(encrypted).ToArray();
}
}
// Batch generation for high-volume scenarios
public async Task<List<GeneratedBarcode>> GenerateBatchQRCodesAsync(List<string> data, QRCodeOptions options)
{
var tasks = data.Select(item => Task.Run(() =>
{
var qr = QRCodeWriter.CreateQrCode(
item,
options.Size,
options.ErrorCorrection
);
if (options.IncludeMargins)
qr.SetMargins(options.MarginSize);
if (options.CustomColor.HasValue)
qr.ChangeBarCodeColor(options.CustomColor.Value);
return qr;
}));
return (await Task.WhenAll(tasks)).ToList();
}
}
// Supporting models
public class AssetInfo
{
public string AssetId { get; set; }
public string AssetType { get; set; }
public string CurrentLocation { get; set; }
public DateTime LastMaintenanceDate { get; set; }
}
public class VisitorInfo
{
public string Name { get; set; }
public string Company { get; set; }
public string HostEmployee { get; set; }
public DateTime CheckInTime { get; set; }
public string AccessLevel { get; set; }
}
public class ShippingInfo
{
public string TrackingNumber { get; set; }
public string SenderAddress { get; set; }
public string RecipientAddress { get; set; }
public string PostalCode { get; set; }
public string ServiceType { get; set; }
public decimal Weight { get; set; }
}
public class DocumentInfo
{
public string Id { get; set; }
public string DocumentType { get; set; }
public DateTime CreatedDate { get; set; }
public string Content { get; set; }
}
public class QRCodeOptions
{
public int Size { get; set; } = 400;
public QRCodeWriter.QrErrorCorrectionLevel ErrorCorrection { get; set; } = QRCodeWriter.QrErrorCorrectionLevel.Medium;
public bool IncludeMargins { get; set; } = true;
public int MarginSize { get; set; } = 10;
public Color? CustomColor { get; set; }
}
public class BarcodeGenerationService
{
private readonly ILogger<BarcodeGenerationService> _logger;
// Generate QR codes for asset tracking
public byte[] GenerateAssetQRCode(AssetInfo asset)
{
// Create JSON payload with asset information
var assetData = JsonSerializer.Serialize(new
{
Id = asset.AssetId,
Type = asset.AssetType,
Location = asset.CurrentLocation,
LastMaintenance = asset.LastMaintenanceDate,
Url = "___PROTECTED_URL_45___"
});
// Generate QR code with high error correction for durability
var qrCode = QRCodeWriter.CreateQrCode(assetData, 500, QRCodeWriter.QrErrorCorrectionLevel.High);
// Add company branding
qrCode.AddAnnotationTextAboveBarcode($"ASSET: {asset.AssetId}");
qrCode.AddAnnotationTextBelowBarcode(asset.AssetType);
qrCode.SetMargins(10);
// Style for printing on asset labels
qrCode.ChangeBarCodeColor(Color.Black);
qrCode.ChangeBackgroundColor(Color.White);
return qrCode.ToStream().ToArray();
}
// Generate visitor badges with QR codes
public GeneratedBarcode CreateVisitorBadgeQR(VisitorInfo visitor)
{
// Encode visitor information with expiration
var visitorData = new
{
Name = visitor.Name,
Company = visitor.Company,
Host = visitor.HostEmployee,
ValidFrom = visitor.CheckInTime,
ValidUntil = visitor.CheckInTime.AddHours(8),
AccessLevel = visitor.AccessLevel,
BadgeId = Guid.NewGuid().ToString()
};
var qrCode = QRCodeWriter.CreateQrCode(
JsonSerializer.Serialize(visitorData),
400,
QRCodeWriter.QrErrorCorrectionLevel.Medium
);
// Add company logo for professional appearance
if (File.Exists("company-logo.png"))
{
qrCode.AddLogo("company-logo.png");
}
// Style for badge printing
qrCode.SetMargins(15);
qrCode.ChangeBarCodeColor(Color.FromArgb(0, 48, 135)); // Company blue
// Add visible text for security personnel
qrCode.AddAnnotationTextAboveBarcode($"VISITOR: {visitor.Name}");
qrCode.AddAnnotationTextBelowBarcode($"Expires: {visitor.CheckInTime.AddHours(8):HH:mm}");
_logger.LogInformation($"Generated visitor badge for {visitor.Name}, BadgeId: {visitorData.BadgeId}");
return qrCode;
}
// Generate shipping labels with multiple barcodes
public byte[] GenerateShippingLabel(ShippingInfo shipping)
{
// Create a PDF with multiple barcodes
var pdf = new IronPdf.ChromePdfRenderer();
// Generate tracking barcode (Code 128 for USPS/UPS compatibility)
var trackingBarcode = BarcodeWriter.CreateBarcode(
shipping.TrackingNumber,
BarcodeEncoding.Code128
);
trackingBarcode.ResizeTo(300, 75);
trackingBarcode.SetMargins(5);
// Generate postal code barcode
var postalBarcode = BarcodeWriter.CreateBarcode(
shipping.PostalCode,
BarcodeEncoding.Code128
);
postalBarcode.ResizeTo(200, 50);
// Generate QR code with complete shipping data
var shippingData = JsonSerializer.Serialize(shipping);
var qrCode = QRCodeWriter.CreateQrCode(shippingData, 200);
// Combine into shipping label HTML
var labelHtml = $@
<html>
<body style='font-family: Arial, sans-serif;'>
<div style='border: 2px solid black; padding: 20px; width: 4in; height: 6in;'>
<h2>SHIPPING LABEL</h2>
<hr/>
<p><strong>From:</strong><br/>{shipping.SenderAddress}</p>
<p><strong>To:</strong><br/>{shipping.RecipientAddress}</p>
<hr/>
<div style='text-align: center;'>
<img src='{trackingBarcode.ToDataUrl()}' alt='Tracking barcode'/>
<p>Tracking: {shipping.TrackingNumber}</p>
</div>
<div style='margin-top: 20px;'>
<img src='{postalBarcode.ToDataUrl()}' alt='Postal barcode' style='float: left;'/>
<img src='{qrCode.ToDataUrl()}' alt='Shipping QR code' style='float: right; width: 100px;'/>
</div>
<div style='clear: both; margin-top: 20px; font-size: 10px;'>
<p>Service: {shipping.ServiceType} | Weight: {shipping.Weight}lbs</p>
</div>
</div>
</body>
</html>";
return pdf.RenderHtmlAsPdf(labelHtml).BinaryData;
}
// Generate secure document QR codes with encryption
public class SecureDocumentQR
{
private readonly byte[] _encryptionKey;
public SecureDocumentQR(byte[] encryptionKey)
{
_encryptionKey = encryptionKey;
}
public GeneratedBarcode GenerateSecureDocumentQR(DocumentInfo document)
{
// Create document reference with security features
var documentRef = new
{
DocumentId = document.Id,
Type = document.DocumentType,
CreatedDate = document.CreatedDate,
Hash = ComputeDocumentHash(document),
AccessUrl = "___PROTECTED_URL_46___",
ValidUntil = DateTime.UtcNow.AddDays(30)
};
// Encrypt sensitive data
var jsonData = JsonSerializer.Serialize(documentRef);
var encryptedData = EncryptData(jsonData);
// Generate QR with encrypted payload
var qrCode = QRCodeWriter.CreateQrCode(
Convert.ToBase64String(encryptedData),
500,
QRCodeWriter.QrErrorCorrectionLevel.High
);
// Add visual security indicators
qrCode.ChangeBarCodeColor(Color.DarkGreen);
qrCode.AddAnnotationTextAboveBarcode("SECURE DOCUMENT");
qrCode.AddAnnotationTextBelowBarcode($"Expires: {documentRef.ValidUntil:yyyy-MM-dd}");
return qrCode;
}
private string ComputeDocumentHash(DocumentInfo document)
{
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(document.Content ?? document.Id));
return BitConverter.ToString(hash).Replace("-", "");
}
private byte[] EncryptData(string data)
{
// Simplified encryption - use proper encryption in production
using var aes = Aes.Create();
aes.Key = _encryptionKey;
aes.GenerateIV();
using var encryptor = aes.CreateEncryptor();
var dataBytes = Encoding.UTF8.GetBytes(data);
var encrypted = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length);
// Prepend IV for decryption
return aes.IV.Concat(encrypted).ToArray();
}
}
// Batch generation for high-volume scenarios
public async Task<List<GeneratedBarcode>> GenerateBatchQRCodesAsync(List<string> data, QRCodeOptions options)
{
var tasks = data.Select(item => Task.Run(() =>
{
var qr = QRCodeWriter.CreateQrCode(
item,
options.Size,
options.ErrorCorrection
);
if (options.IncludeMargins)
qr.SetMargins(options.MarginSize);
if (options.CustomColor.HasValue)
qr.ChangeBarCodeColor(options.CustomColor.Value);
return qr;
}));
return (await Task.WhenAll(tasks)).ToList();
}
}
// Supporting models
public class AssetInfo
{
public string AssetId { get; set; }
public string AssetType { get; set; }
public string CurrentLocation { get; set; }
public DateTime LastMaintenanceDate { get; set; }
}
public class VisitorInfo
{
public string Name { get; set; }
public string Company { get; set; }
public string HostEmployee { get; set; }
public DateTime CheckInTime { get; set; }
public string AccessLevel { get; set; }
}
public class ShippingInfo
{
public string TrackingNumber { get; set; }
public string SenderAddress { get; set; }
public string RecipientAddress { get; set; }
public string PostalCode { get; set; }
public string ServiceType { get; set; }
public decimal Weight { get; set; }
}
public class DocumentInfo
{
public string Id { get; set; }
public string DocumentType { get; set; }
public DateTime CreatedDate { get; set; }
public string Content { get; set; }
}
public class QRCodeOptions
{
public int Size { get; set; } = 400;
public QRCodeWriter.QrErrorCorrectionLevel ErrorCorrection { get; set; } = QRCodeWriter.QrErrorCorrectionLevel.Medium;
public bool IncludeMargins { get; set; } = true;
public int MarginSize { get; set; } = 10;
public Color? CustomColor { get; set; }
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Security.Cryptography
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class BarcodeGenerationService
Private ReadOnly _logger As ILogger(Of BarcodeGenerationService)
' Generate QR codes for asset tracking
Public Function GenerateAssetQRCode(asset As AssetInfo) As Byte()
' Create JSON payload with asset information
Dim assetData = JsonSerializer.Serialize(New With {
.Id = asset.AssetId,
.Type = asset.AssetType,
.Location = asset.CurrentLocation,
.LastMaintenance = asset.LastMaintenanceDate,
.Url = "___PROTECTED_URL_45___"
})
' Generate QR code with high error correction for durability
Dim qrCode = QRCodeWriter.CreateQrCode(assetData, 500, QRCodeWriter.QrErrorCorrectionLevel.High)
' Add company branding
qrCode.AddAnnotationTextAboveBarcode($"ASSET: {asset.AssetId}")
qrCode.AddAnnotationTextBelowBarcode(asset.AssetType)
qrCode.SetMargins(10)
' Style for printing on asset labels
qrCode.ChangeBarCodeColor(Color.Black)
qrCode.ChangeBackgroundColor(Color.White)
Return qrCode.ToStream().ToArray()
End Function
' Generate visitor badges with QR codes
Public Function CreateVisitorBadgeQR(visitor As VisitorInfo) As GeneratedBarcode
' Encode visitor information with expiration
Dim visitorData = New With {
.Name = visitor.Name,
.Company = visitor.Company,
.Host = visitor.HostEmployee,
.ValidFrom = visitor.CheckInTime,
.ValidUntil = visitor.CheckInTime.AddHours(8),
.AccessLevel = visitor.AccessLevel,
.BadgeId = Guid.NewGuid().ToString()
}
Dim qrCode = QRCodeWriter.CreateQrCode(
JsonSerializer.Serialize(visitorData),
400,
QRCodeWriter.QrErrorCorrectionLevel.Medium
)
' Add company logo for professional appearance
If File.Exists("company-logo.png") Then
qrCode.AddLogo("company-logo.png")
End If
' Style for badge printing
qrCode.SetMargins(15)
qrCode.ChangeBarCodeColor(Color.FromArgb(0, 48, 135)) ' Company blue
' Add visible text for security personnel
qrCode.AddAnnotationTextAboveBarcode($"VISITOR: {visitor.Name}")
qrCode.AddAnnotationTextBelowBarcode($"Expires: {visitor.CheckInTime.AddHours(8):HH:mm}")
_logger.LogInformation($"Generated visitor badge for {visitor.Name}, BadgeId: {visitorData.BadgeId}")
Return qrCode
End Function
' Generate shipping labels with multiple barcodes
Public Function GenerateShippingLabel(shipping As ShippingInfo) As Byte()
' Create a PDF with multiple barcodes
Dim pdf = New IronPdf.ChromePdfRenderer()
' Generate tracking barcode (Code 128 for USPS/UPS compatibility)
Dim trackingBarcode = BarcodeWriter.CreateBarcode(
shipping.TrackingNumber,
BarcodeEncoding.Code128
)
trackingBarcode.ResizeTo(300, 75)
trackingBarcode.SetMargins(5)
' Generate postal code barcode
Dim postalBarcode = BarcodeWriter.CreateBarcode(
shipping.PostalCode,
BarcodeEncoding.Code128
)
postalBarcode.ResizeTo(200, 50)
' Generate QR code with complete shipping data
Dim shippingData = JsonSerializer.Serialize(shipping)
Dim qrCode = QRCodeWriter.CreateQrCode(shippingData, 200)
' Combine into shipping label HTML
Dim labelHtml = $"
<html>
<body style='font-family: Arial, sans-serif;'>
<div style='border: 2px solid black; padding: 20px; width: 4in; height: 6in;'>
<h2>SHIPPING LABEL</h2>
<hr/>
<p><strong>From:</strong><br/>{shipping.SenderAddress}</p>
<p><strong>To:</strong><br/>{shipping.RecipientAddress}</p>
<hr/>
<div style='text-align: center;'>
<img src='{trackingBarcode.ToDataUrl()}' alt='Tracking barcode'/>
<p>Tracking: {shipping.TrackingNumber}</p>
</div>
<div style='margin-top: 20px;'>
<img src='{postalBarcode.ToDataUrl()}' alt='Postal barcode' style='float: left;'/>
<img src='{qrCode.ToDataUrl()}' alt='Shipping QR code' style='float: right; width: 100px;'/>
</div>
<div style='clear: both; margin-top: 20px; font-size: 10px;'>
<p>Service: {shipping.ServiceType} | Weight: {shipping.Weight}lbs</p>
</div>
</div>
</body>
</html>"
Return pdf.RenderHtmlAsPdf(labelHtml).BinaryData
End Function
' Generate secure document QR codes with encryption
Public Class SecureDocumentQR
Private ReadOnly _encryptionKey As Byte()
Public Sub New(encryptionKey As Byte())
_encryptionKey = encryptionKey
End Sub
Public Function GenerateSecureDocumentQR(document As DocumentInfo) As GeneratedBarcode
' Create document reference with security features
Dim documentRef = New With {
.DocumentId = document.Id,
.Type = document.DocumentType,
.CreatedDate = document.CreatedDate,
.Hash = ComputeDocumentHash(document),
.AccessUrl = "___PROTECTED_URL_46___",
.ValidUntil = DateTime.UtcNow.AddDays(30)
}
' Encrypt sensitive data
Dim jsonData = JsonSerializer.Serialize(documentRef)
Dim encryptedData = EncryptData(jsonData)
' Generate QR with encrypted payload
Dim qrCode = QRCodeWriter.CreateQrCode(
Convert.ToBase64String(encryptedData),
500,
QRCodeWriter.QrErrorCorrectionLevel.High
)
' Add visual security indicators
qrCode.ChangeBarCodeColor(Color.DarkGreen)
qrCode.AddAnnotationTextAboveBarcode("SECURE DOCUMENT")
qrCode.AddAnnotationTextBelowBarcode($"Expires: {documentRef.ValidUntil:yyyy-MM-dd}")
Return qrCode
End Function
Private Function ComputeDocumentHash(document As DocumentInfo) As String
Using sha256 = SHA256.Create()
Dim hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(If(document.Content, document.Id)))
Return BitConverter.ToString(hash).Replace("-", "")
End Using
End Function
Private Function EncryptData(data As String) As Byte()
' Simplified encryption - use proper encryption in production
Using aes = Aes.Create()
aes.Key = _encryptionKey
aes.GenerateIV()
Using encryptor = aes.CreateEncryptor()
Dim dataBytes = Encoding.UTF8.GetBytes(data)
Dim encrypted = encryptor.TransformFinalBlock(dataBytes, 0, dataBytes.Length)
' Prepend IV for decryption
Return aes.IV.Concat(encrypted).ToArray()
End Using
End Using
End Function
End Class
' Batch generation for high-volume scenarios
Public Async Function GenerateBatchQRCodesAsync(data As List(Of String), options As QRCodeOptions) As Task(Of List(Of GeneratedBarcode))
Dim tasks = data.Select(Function(item) Task.Run(Function()
Dim qr = QRCodeWriter.CreateQrCode(
item,
options.Size,
options.ErrorCorrection
)
If options.IncludeMargins Then
qr.SetMargins(options.MarginSize)
End If
If options.CustomColor.HasValue Then
qr.ChangeBarCodeColor(options.CustomColor.Value)
End If
Return qr
End Function))
Return (Await Task.WhenAll(tasks)).ToList()
End Function
End Class
' Supporting models
Public Class AssetInfo
Public Property AssetId As String
Public Property AssetType As String
Public Property CurrentLocation As String
Public Property LastMaintenanceDate As DateTime
End Class
Public Class VisitorInfo
Public Property Name As String
Public Property Company As String
Public Property HostEmployee As String
Public Property CheckInTime As DateTime
Public Property AccessLevel As String
End Class
Public Class ShippingInfo
Public Property TrackingNumber As String
Public Property SenderAddress As String
Public Property RecipientAddress As String
Public Property PostalCode As String
Public Property ServiceType As String
Public Property Weight As Decimal
End Class
Public Class DocumentInfo
Public Property Id As String
Public Property DocumentType As String
Public Property CreatedDate As DateTime
Public Property Content As String
End Class
Public Class QRCodeOptions
Public Property Size As Integer = 400
Public Property ErrorCorrection As QRCodeWriter.QrErrorCorrectionLevel = QRCodeWriter.QrErrorCorrectionLevel.Medium
Public Property IncludeMargins As Boolean = True
Public Property MarginSize As Integer = 10
Public Property CustomColor As Color?
End Class
Estos ejemplos de generación demuestran casos de uso específicos de la empresa. Los códigos QR de seguimiento de activos incluyen el historial de mantenimiento y enlaces directos a los sistemas de gestión de activos. Las credenciales de visitante incorporan tiempos de vencimiento y niveles de acceso para cumplir con la seguridad. Las etiquetas de envío combinan múltiples formatos de códigos de barras para cumplir con los requisitos del transportista.
La implementación del documento seguro QR muestra cómo integrar el cifrado para datos confidenciales. Este enfoque garantiza que incluso si alguien captura el código QR, no podrá acceder a la información sin las claves de descifrado adecuadas. Esto resulta esencial para documentos de cumplimiento, registros financieros o comunicaciones confidenciales.
¿Cuales son tus próximos pasos?
La creación de una API de escáner de código de barras empresarial con IronBarcode proporciona una base confiable para las iniciativas de transformación digital. La combinación de soporte de formato completo, procesamiento de imágenes avanzado y compatibilidad multiplataforma aborda los complejos requisitos de las empresas modernas. Antes de finalizar su arquitectura, revisar la documentación de los estándares de códigos de barras GS1 le ayudará a comprender los requisitos de codificación para los códigos de barras de la cadena de suministro y minoristas. Para los patrones de diseño de API REST utilizados en los ejemplos de controlador anteriores, la guía de diseño de API RESTful de restfulapi .NET proporciona una referencia práctica. Al realizar implementaciones en entornos de contenedores, la documentación oficial de Docker sobre imágenes .NET es la fuente autorizada para la selección de imágenes base.
Puntos clave para su implementación:
- La seguridad es lo primero : implementar la autenticación, la validación de entrada y el registro de auditoría adecuados desde el principio.
- Optimización del rendimiento : utilice velocidades de escaneo y estrategias de almacenamiento en caché adecuadas para su caso de uso.
- Manejo de errores : Construya sistemas resilientes que gestionen con elegancia los casos extremos y brinden retroalimentación significativa.
- Escalabilidad : Diseño con escalabilidad horizontal en mente, utilizando patrones asincrónicos y gestión eficiente de recursos.
Para implementaciones de producción, considere estos recursos adicionales:
- Revise el registro de cambios para conocer las últimas funciones y mejoras.
- Explorar la compatibilidad con nuevos formatos, incluidos formatos empresariales especializados
- Implementar la implementación del instalador MSI para aplicaciones de escritorio
- Configurar las claves de licencia correctamente para entornos de producción
Las funciones empresariales de IronBarcode se extienden más allá del escaneo básico. Las capacidades de personalización de estilo de la biblioteca permiten la generación de códigos de barras de marca. La compatibilidad con códigos de barras Unicode garantiza compatibilidad global. Las funciones avanzadas, como los márgenes de código de barras y los niveles de corrección de errores, proporcionan un control detallado sobre la calidad de salida.
Soporte y recursos empresariales
A la hora de implementar soluciones de código de barras de misión crítica, es importante contar con un soporte confiable. IronBarcode ofrece:
- Soporte empresarial dedicado con garantías de SLA
- Soporte de solicitudes de ingeniería para requisitos personalizados
- Actualizaciones de seguridad periódicas y monitoreo de CVE
- Guías completas de solución de problemas para situaciones comunes
El proceso desde el manejo manual de códigos de barras hasta el procesamiento automatizado impulsado por API transforma la eficiencia operativa. Ya sea que esté procesando miles de documentos de envío, administrando el acceso de visitantes o rastreando activos empresariales, IronBarcode proporciona la confiabilidad y las características que exigen los entornos empresariales.
¿Está listo para transformar su procesamiento de códigos de barras? Comience con una prueba gratuita y experimente la diferencia que puede generar el procesamiento profesional de códigos de barras. La documentación completa de la API proporciona una guía de implementación detallada, mientras que la sección de ejemplos ofrece código listo para usar para escenarios comunes.
Su empresa merece una solución de código de barras que se adapte a sus necesidades, mantenga el cumplimiento de la seguridad y ofrezca resultados consistentes en todas las plataformas. IronBarcode ofrece exactamente eso: respaldado por una empresa comprometida con el soporte a largo plazo y la mejora continua. Comience a construir su API de escáner hoy y únase a miles de empresas que confían en IronBarcode para sus necesidades críticas de procesamiento de códigos de barras.
Preguntas Frecuentes
¿Cuál es el principal beneficio de usar IronBarcode para construir una API de escáner en C#?
IronBarcode permite a los desarrolladores crear rápidamente una poderosa API de escáner de códigos de barras lista para producción con mínima complejidad. Simplifica el proceso eliminando la necesidad de integraciones complejas con SDK de escáner.
¿Puede IronBarcode manejar entradas de códigos de barras dañadas?
Sí, IronBarcode está diseñado para procesar datos de códigos de barras incluso de entradas de escaneo dañadas, asegurando alta fiabilidad en aplicaciones del mundo real.
¿Qué tipos de entradas puede IronBarcode procesar en una API de escáner en C#?
IronBarcode puede procesar datos de códigos de barras de varias entradas como imágenes y PDFs, ofreciendo soluciones versátiles para diferentes necesidades de escaneo.
¿Hay un tutorial disponible para construir una API de escáner de códigos de barras usando IronBarcode?
Sí, la página web proporciona un tutorial completo con ejemplos de código para guiar a los desarrolladores en la construcción de un punto final de escaneo de códigos de barras RESTful usando IronBarcode.
¿Qué tan rápido se puede configurar una API de escáner de códigos de barras usando IronBarcode?
Con IronBarcode, los desarrolladores pueden configurar una API de escáner de códigos de barras en minutos, optimizando el tiempo y esfuerzo de desarrollo.
¿Requiere IronBarcode alguna integración compleja de SDK?
No, IronBarcode elimina la necesidad de integraciones complejas con SDK de escáner, facilitando a los desarrolladores implementar la funcionalidad de escaneo de códigos de barras.
¿Qué lenguaje de programación se utiliza con IronBarcode para construir una API de escáner?
IronBarcode se utiliza con C# para construir una API de escáner, aprovechando el marco .NET para un rendimiento robusto.


