使用 IronBarcode 建立 C# 掃描器 API
使用 IronBarcode 和 ASP.NET Core 建立一個 RESTful 條碼掃描器 API,無需硬體即可管理來自影像、PDF 和損壞掃描的條碼,確保在 Windows、Linux 和 macOS 上具有可靠的效能。
IronBarcode 透過 ASP.NET Core 中的 RESTful API 實現專業的條碼掃描,無需硬體依賴即可處理影像、PDF 和損壞的掃描件,同時支援跨平台部署,並具有內建的安全性和合規性功能。
企業條碼挑戰
想像一下,你是財富 500 強物流公司的企業架構師。 您的倉庫團隊正為損壞的運輸標籤而苦惱,您的財務部門需要處理數千份帶有嵌入式條碼的發票 PDF 文件,而您的 IT 安全團隊則要求找到一種符合 SOC2 合規性且不會在您的全球基礎架構中引入硬體依賴性的解決方案。
這種情況促使我們的一位企業客戶發現了IronBarcode的 REST API 功能。 最初只是一個簡單的讀取條碼的需求,後來發展成為一個完整的掃描解決方案,現在每月在整個組織內處理超過 200 萬個條碼。
傳統上,這需要在每個地點配備昂貴的硬體掃描器、複雜的驅動程式管理以及大量的安全審查。 相反,他們建立了一個集中式 RESTful API,任何部門都可以安全地存取該 API,無論是掃描損壞的倉庫標籤還是從供應商 PDF 中提取發票資料。
本教學將引導您建立相同的專業條碼掃描器 API。 您將學習如何創建安全的端點,以應對現實世界的挑戰,例如撕破的標籤、傾斜的圖像和多頁文件。 更重要的是,您將了解如何在滿足企業環境所需的安全性、可擴展性和供應商穩定性的前提下實現這一點。
建立可靠的 C# 掃描器 API
建立企業級條碼掃描器 API 需要預先解決幾個關鍵問題。 安全團隊需要對資料處理方式得到保證。 合規官需要審計追蹤記錄。 維運團隊要求不同平台之間具備可靠性。 IronBarcode 的完整文件有系統地解決了所有這些問題。
RESTful 方法的優點在於其簡潔性和安全性。 透過將條碼處理集中在安全的 API 端點之後,您可以完全控制資料流。 用戶端設備無需直接存取您的條碼處理邏輯。 該架構自然支援您的零信任安全模型,同時能夠與現有企業系統無縫整合。
讓我們從基礎開始。 IronBarcode 的讀取功能支援您的企業可能遇到的所有主要條碼格式。 從製造業中使用的傳統Code 39 條碼到行銷活動中使用的現代二維碼,該程式庫都能處理。 支援的條碼格式指南為您的實施方案規劃提供了完整的參考。
該庫的容錯能力使其在企業場景中特別有價值。 現實世界中的條碼並不完美。 它們可能會損壞、印刷品質差或掃描角度不對。 IronBarcode 的先進影像處理技術可自動應對這些挑戰,從而減少支援工單並提高營運效率。
安裝和設定 IronBarcode
企業部署需要仔細考慮依賴關係和授權問題。 IronBarcode 以簡單的 NuGet 安裝和透明的許可方式簡化了這個過程。 以下是如何開始您的 ASP.NET Core 專案:
Install-Package IronBarCodeInstall-Package IronBarCode對於有特定平台需求的企業環境,請參閱進階 NuGet 安裝指南。 這涵蓋了諸如針對特定 .NET 版本或針對特定平台進行最佳化等場景。
安裝完成後,透過簡單的測試來驗證您的設定。以下程式碼示範了該程式庫在極少配置下讀取條碼的能力:
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}");
}
}
}此驗證步驟對於企業部署至關重要。 它確認該庫已正確安裝、獲得許可,並且可以存取必要的系統資源。 置信度評分輸出有助於為您的特定條碼類型建立基準預期。
對於生產環境部署,請仔細閱讀許可文件。 企業許可證支援在您的組織內進行無限次部署,這對於跨多個資料中心進行擴展至關重要。 許可證金鑰配置指南解釋了各種啟動方法,包括符合您的DevOps實務的環境變數和設定檔。
企業平台相容性
您的企業很可能運作的是一個異質環境。 開發工作在 Windows 系統上進行,測試環境運行在 Linux 容器上,有些團隊可能會使用 macOS 系統。 IronBarcode 的跨平台相容性確保了在所有這些平台上行為的一致性。
對於容器化部署, Docker 設定指南提供了針對 Linux 容器的具體說明。 這包括處理依賴關係和優化鏡像大小以實現高效的雲端部署。 如果您要部署到Azure或AWS Lambda ,平台特定的指南可確保在這些環境中獲得最佳效能。
該庫甚至透過.NET MAUI 整合支援行動場景,使您能夠在現場工作人員需要行動掃描功能時,將條碼掃描功能擴展到iOS和Android設備。
建立完整的掃描器 API 控制器
讓我們建立一個能夠滿足企業實際需求、可用於生產環境的掃描器 API。 此實現方案包含了適當的錯誤處理、安全考量和效能最佳化:
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"
};
}
}
}此專業版本包含生產部署所需的多項關鍵功能。身份驗證可確保只有授權使用者才能存取掃描服務。請求大小限制可防止拒絕服務攻擊。 完整的日誌記錄為合規團隊提供了所需的審計追蹤。
結構化回應模型不僅傳回條碼值,還傳回置信度分數和位置資料。 這些元資料對於品質保證流程來說非常寶貴。 當供應商的條碼掃描置信度較低時,您的系統可以將其標記為需要人工審核。
專業錯誤處理
請注意,控制器從未向客戶端公開內部異常詳細資訊。 這符合安全最佳實踐,可以防止資訊外洩。 詳細的日誌記錄可以捕獲調試所需的所有信息,同時保持錯誤回應的通用性。
RequestId欄位支援跨分散式系統進行端到端追蹤。 當倉庫工人回報掃描問題時,您的支援團隊可以快速在集中式日誌系統中找到確切的請求。 這大大縮短了生產問題的平均解決時間(MTTR)。
透過ProcessingTimeMs進行效能監控有助於識別瓶頸。 如果某些條碼類型的處理時間總是較長,您可以相應地調整讀取速度設定。 閱讀速度範例展示了不同的設定如何影響效能。
處理不同的輸入來源
企業系統很少採用單一標準格式。 您的掃描器 API 需要處理從高解析度倉庫攝影機到幾十年前傳真機產生的低品質 PDF 掃描件的所有資料。 以下是如何建構這種靈活性的方法:
// 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; }
}這個完整實施方案針對的是現實世界的企業場景。 URL 掃描端點包含網域驗證,以防止伺服器端要求偽造 (SSRF) 攻擊。 Base64 處理程序會移除資料 URL 前綴,這在從 Web 應用程式接收圖片時很常見。
損壞的條碼端點展示了 IronBarcode 的影像校正功能。 透過應用多種濾波器並嘗試不同的方法,它可以從嚴重退化的圖像中恢復資料。 在處理舊的庫存標籤或受天氣損壞的運輸單據時,這非常有用。
選擇PDF掃描還是影像掃描
PDF 檔案為企業條碼掃描帶來了獨特的挑戰和機會。 許多商業文件,如發票、發貨清單和合規表格,都是以帶有嵌入式條碼的 PDF 格式提供的。 IronBarcode 的PDF 條碼讀取功能可以很好地處理這些情況。
設想一個典型的企業情境:您的應付帳款部門每月收到數千張發票,每張發票都包含用於自動處理的條碼。 有些供應商發送高品質的PDF文件,有些供應商發送品質參差不齊的掃描文件。
進階PDF掃描終端可以處理這兩種情況。 對於高品質 PDF 文件,標準設定即可完美滿足需求。 對於掃描文檔,提高 DPI 和縮放參數可以提高偵測率。 頁面特定處理功能可讓您從特定頁面提取條碼,當您知道發票的第一頁總是有條碼時,此功能非常有用。
按頁面分組的結果提供了有價值的背景資訊。 您的工作流程系統可以根據哪些頁麵包含條碼來路由文檔,從而實現文檔分類的自動化。 這種結構化的多頁文件處理方法將手動流程轉變為高效的自動化工作流程。
高效率處理多個條碼
企業文件通常包含多個條碼,用於不同的用途。 運輸標籤可能包含追蹤條碼、產品條碼和目的地條碼。 妥善處理多種條碼應用程式場景需要周詳的 API 設計。
批次處理端點示範如何有效率地處理大容量場景。 透過並行處理多個文件,可以顯著縮短總處理時間。 Task.WhenAll 模式可確保最佳資源利用率WhenAll同時維持系統穩定性。
對於在您的應用程式中建立條碼,IronBarcode 提供了同樣有效的功能。 無論您是需要為庫存系統產生一維線性條碼,還是為行動應用程式建立二維矩陣碼,都適用相同的簡單 API 方法。
提高性能和準確性
企業條碼掃描的效能最佳化不僅僅是速度的問題,而是要找到適合您特定用例的合適平衡點。 讓我們來探索能夠顯著提升掃描效能的進階最佳化技術:
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; }
}這種先進的最佳化方法展示了多種專業技術。 智慧型設定確定功能透過分析影像特性自動選擇最佳處理參數。 現代相機拍攝的高解析度影像可以使用更快的處理速度,而低品質掃描影像則可以進行增強的濾波處理。
區域掃描對於大幅面文件尤其有效。 透過將影像分割成多個區域並並行處理,可以顯著提高效能。 這種方法對於條碼位置可預測的標準化表格尤其有效。
選擇合適的ReadingSpeed設定
自適應增強方法會逐步嘗試更激進的處理技術,直到成功為止。 這樣既能確保高品質條碼得到最快的處理,又能成功處理損壞的條碼。 置信度閾值功能利用機器學習來驗證結果,從而減少生產環境中的誤報。
針對特定最佳化場景:
-大批量處理:使用ReadingSpeed.Faster並進行特定條碼類型篩選 -混合品質文件:首先使用ReadingSpeed.Balanced ,然後實施自適應增強 -條碼損壞或老化:請使用完整影像濾鏡的ReadingSpeed.ExtremeDetail -即時應用:實施快取和基於區域的掃描,以確保效能穩定。
基準測試服務可協助您根據數據做出配置決策。 使用實際條碼樣本進行基準測試,以找到適合您特定用例的速度和準確性之間的最佳平衡點。
影像濾波器的影響
影像濾波器可以顯著提高複雜條碼的辨識率。 影像校正文件解釋了每個濾波器的用途。 但是,濾鏡也會增加處理時間,所以要謹慎使用。
對於企業部署,可以考慮採用分層方法:
- 首次嘗試快速掃描,不使用過濾器
- 如果第一次嘗試失敗,則套用基本過濾器。
- 僅在必要時才使用強力過濾。
- 快取結果以避免重複處理
該策略在保持快速平均回應時間的同時,也能成功處理疑難案件。 對於使用者可能以各種角度握持裝置的行動裝置拍攝的照片而言,方向校正功能尤其有價值。
在 C# 中建立二維碼圖像
掃描固然重要,但許多企業應用程式也需要產生條碼。 IronBarcode 讓建立條碼就像讀取條碼一樣簡單。 以下是如何在各種企業場景下產生二維碼的方法:
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; }
}這些生成範例展示了企業特定的使用案例。 資產追蹤二維碼包含維護歷史記錄和與資產管理系統的直接連結。 訪客證包含有效期限和存取權限級別,以符合安全規定。 運輸標籤結合了多種條碼格式,以滿足承運商的要求。
安全文檔二維碼實作展示如何為敏感資料整合加密。 這種方法確保即使有人取得了二維碼,也沒有正確的解密金鑰,也無法存取其中的資訊。 這對於合規文件、財務記錄或機密通訊至關重要。
生產部署的後續步驟
使用 IronBarcode 建立企業級條碼掃描器 API 為數位轉型計畫提供了可靠的基礎。 全面的格式支援、先進的影像處理和跨平台相容性相結合,滿足了現代企業的複雜需求。
實施過程中需要注意的關鍵要點:
1.安全第一:從一開始就實施適當的身份驗證、輸入驗證和稽核日誌記錄 2.效能最佳化:根據您的使用場景選擇合適的掃描速度和快取策略 3.錯誤處理:建構能夠優雅地處理極端情況並提供有效回饋的彈性系統 4.可擴展性:設計時要考慮橫向擴展,利用非同步模式和高效率的資源管理
對於生產環境部署,請考慮以下額外資源:
查看更新日誌,了解最新功能和改進
- 探索新的格式支持,包括專門的企業格式
- 實作桌面應用程式的MSI 安裝程式部署
- 為生產環境正確配置許可證密鑰
IronBarcode 的企業級功能不僅限於基本的掃描功能。 此庫的樣式自訂功能支援產生品牌條碼。 支援Unicode條碼可確保全球相容性。 條碼邊距和糾錯等級等進階功能可對輸出品質進行精細控制。
企業支持和資源
在實施關鍵任務條碼解決方案時,擁有可靠的支援至關重要。 IronBarcode提供:
從人工條碼處理到自動化 API 驅動的處理流程,徹底改變了營運效率。 無論您是處理數千份貨運單據、管理訪客存取權限,還是追蹤企業資產,IronBarcode 都能提供企業環境所需的可靠性和功能。
準備好革新您的條碼處理流程了嗎? 立即開始免費試用,體驗專業條碼處理帶來的不同。 完整的API 文件提供了詳細的實作指南,而範例部分則提供了常見場景的即用程式碼。
您的企業需要一個能夠隨著您的需求擴展、保持安全合規性並在所有平台上提供一致結果的條碼解決方案。 IronBarcode 正是為此而生——公司致力於長期支持和持續改進。 立即開始建立您的掃描器 API,加入數千家信賴 IronBarcode 以滿足其關鍵條碼處理需求的企業行列。
常見問題解答
使用 IronBarcode 在 C# 中建立掃描器 API 的主要優勢是什麼?
IronBarcode 讓開發人員能夠以最小的複雜度快速建立功能強大、可用於生產環境的條碼掃描器 API。它簡化了流程,無需整合複雜的掃描器 SDK。
IronBarcode 能否處理損壞的條碼輸入?
是的,IronBarcode 的設計宗旨是即使掃描輸入損壞也能處理條碼數據,從而確保在實際應用中具有高可靠性。
IronBarcode 的 C# 掃描器 API 可以處理哪些類型的輸入?
IronBarcode 可以處理來自影像和 PDF 等各種輸入的條碼數據,為不同的掃描需求提供多樣化的解決方案。
是否有關於如何使用 IronBarcode 建立條碼掃描器 API 的教學課程?
是的,網頁提供了全面的教學和程式碼範例,指導開發人員使用 IronBarcode 來建立 RESTful 條碼掃描端點。
使用 IronBarcode 可以多快設定好條碼掃描器 API?
借助 IronBarcode,開發人員可以在幾分鐘內設定條碼掃描器 API,從而簡化開發時間和精力。
IronBarcode是否需要複雜的SDK整合?
不,IronBarcode 消除了對複雜掃描器 SDK 整合的需求,使開發人員更容易實現條碼掃描功能。
IronBarcode 使用哪種程式語言來建立掃描器 API?
IronBarcode 與 C# 結合使用,建立掃描器 API,並利用 .NET 框架來實現強大的效能。






