跳至页脚内容
视频

How to Read Barcodes From PDFs in C#

从BarcodeScanning.Native.Maui迁移到IronBarcode

本指南提供了从 BarcodeScanning.Native.Maui 到IronBarcode的完整迁移路径,涵盖了相机事件模式替换、命名空间更改、代码迁移示例以及 BarcodeScanning.Native.Maui 无法处理的场景的处理——Windows MAUI、文件和 PDF 输入、服务器端处理和条形码生成。

为什么要从 BarcodeScanning.MAUI 迁移?

从 BarcodeScanning.Native.Maui 迁移的团队报告了以下触发事件:

Windows MAUI 目标平台要求: BarcodeScanning.Native.Maui 封装了 iOS 和 Android 的原生 API。它目前没有 Windows 版本,也没有相关计划。 如果您的 MAUI 应用同时面向 Windows、iOS 和 Android,则需要一个能够在所有三个目标平台上运行且没有平台特定分支的库。

文件或 PDF 输入已添加到要求中: BarcodeScanning.Native.Maui 仅接受实时摄像头帧。 当用户需要从图库上传图片,或者服务器端端点需要从 PDF 中提取条形码时,该库没有可提供的代码路径。 任何文件或 PDF 条形码应用场景都需要不同的工具。

iOS UPC-A 数据在生产环境中出现错误:苹果公司的 Vision 框架为 UPC-A 条形码(EAN-13 编码)返回 13 位数字。 BarcodeScanning.Native.Maui 会直接传递未经校正的数据。 如果 UPC-A 条码以零开头存储,则库存记录、销售点查询或供应链集成可能会悄无声息地遭到破坏。 IronBarcode无需手动规范化即可返回正确的 12 位 UPC-A 值。

PDF417扫描功能不稳定:图书馆自身的GitHub问题记录显示,PDF417"问题非常严重——大多数扫描都无法完成"。对于货运标签、驾驶执照和登机牌等扫描件而言,这直接阻碍了扫描工作。

需要生成: BarcodeScanning.Native.Maui 无法生成条形码。 IronBarcode可将 Code128、QR、DataMatrix 和其他格式生成为图像文件或字节数组。

引入服务器端处理: BarcodeScanning.Native.Maui 是一个相机 UI 控件——它不能在服务器进程中运行。 当需要同时进行服务器端条形码读取和移动端扫描时, IronBarcode可以使用相同的软件包和相同的 API 来满足这两个方面的需求。

基本问题

BarcodeScanning.Native.Maui 将条形码读取完全与实时摄像头事件模型绑定在一起。 一旦任何需求不符合该模型,该库就无法提供任何功能:

private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode != null)
        ResultLabel.Text = barcode.DisplayValue;
}
private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode != null)
        ResultLabel.Text = barcode.DisplayValue;
}
Private Sub OnBarcodeDetected(sender As Object, e As OnDetectionFinishedEventArg)
    Dim barcode = e.BarcodeResults.FirstOrDefault()
    If barcode IsNot Nothing Then
        ResultLabel.Text = barcode.DisplayValue
    End If
End Sub
$vbLabelText   $csharpLabel

IronBarcode接受任何数据输入——相机拍摄的图像、文件、PDF、字节数组——并且可以在所有平台上运行:

using IronBarCode;

private async void ScanBarcodeButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
using IronBarCode;

private async void ScanBarcodeButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
Imports IronBarCode

Private Async Sub ScanBarcodeButton_Clicked(sender As Object, e As EventArgs)
    Dim photo = Await MediaPicker.CapturePhotoAsync()
    If photo Is Nothing Then Return

    Using stream = Await photo.OpenReadAsync()
        Using ms As New MemoryStream()
            Await stream.CopyToAsync(ms)

            Dim results = BarcodeReader.Read(ms.ToArray())
            ResultLabel.Text = If(results.FirstOrDefault()?.Value, "No barcode found")
        End Using
    End Using
End Sub
$vbLabelText   $csharpLabel

IronBarcode与 BarcodeScanning.MAUI:功能对比

特征 BarcodeScanning.MAUI IronBarcode
实时摄像机帧读取 是的——CameraView 控件 否(使用 MediaPicker 捕获,然后读取)
应用内相机取景器 是的——实时连续 否——使用通过 MediaPicker 的系统相机 UI
从图像文件中读取 是 — BarcodeReader.Read(path)
从字节数组读取数据 是 — BarcodeReader.Read(bytes)
从流中读取 是 — BarcodeReader.Read(stream)
从PDF文件阅读 是 — BarcodeReader.Read(pdf)
条形码生成 是 — BarcodeWriter + QRCodeWriter
Windows MAUI 支持
iOS MAUI 支持
Android MAUI 支持
macOS MAUI 支持 未记录。
服务器端ASP.NET
Docker / Azure / AWS Lambda
iOS UPC-A 准确性 返回 13 位数字(错误),需要手动规范化 返回正确的 12 位 UPC-A
PDF417 可靠性 "大多数扫描从未发生"(GitHub问题) 支持
多条形码检测 是(每个框架多个通过 e.BarcodeResults 是(ExpectMultipleBarcodes 选项)
阅读速度控制 None ReadingSpeed.Faster / Balanced / Detailed / ExtremeDetail
许可 MIT(开源,免费) 商业版——Lite749 美元,Plus1499 美元,Professional2999 美元,无限版 5999 美元
.NET Framework支持 不(仅限毛伊岛) 是的.NET Framework 4.6.2+

快速入门:BarcodeScanning.MAUI 到IronBarcode 的迁移

步骤 1:替换 NuGet 软件包

移除 BarcodeScanning.Native.Maui:

dotnet remove package BarcodeScanning.Native.Maui
dotnet remove package BarcodeScanning.Native.Maui
SHELL

安装IronBarcode:

dotnet add package IronBarcode
dotnet add package IronBarcode
SHELL

步骤 2:更新命名空间

从所有文件中移除 BarcodeScanning 命名空间:

// Remove
using BarcodeScanning;
// Remove
using BarcodeScanning;
Imports BarcodeScanning
$vbLabelText   $csharpLabel

添加IronBarcode命名空间:

// Add
using IronBarCode;
// Add
using IronBarCode;
Imports IronBarCode
$vbLabelText   $csharpLabel

在XAML文件中,移除scanner: XML命名空间声明:


xmlns:scanner="clr-namespace:BarcodeScanning;assembly=BarcodeScanning.Native.Maui"

xmlns:scanner="clr-namespace:BarcodeScanning;assembly=BarcodeScanning.Native.Maui"
XML

步骤 3:初始化许可证

在应用程序启动时添加许可证初始化—在App.xaml.cs

IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronBarCode

IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

代码迁移示例

相机扫描:将 CameraView 转换为 MediaPicker

CameraView 控件提供了带有连续帧检测的实时取景器。 IronBarcode替代品使用MAUI的MediaPicker打开系统相机,捕获照片并处理结果图像。

条形码扫描.MAUI 方法 — XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:scanner="clr-namespace:BarcodeScanning;assembly=BarcodeScanning.Native.Maui">
    <StackLayout>
        <scanner:CameraView x:Name="CameraView"
                            OnDetectionFinished="OnBarcodeDetected"
                            CameraEnabled="True"
                            BarcodeFormats="All"
                            VerticalOptions="FillAndExpand" />
        <Label x:Name="ResultLabel" Text="Waiting for scan..." />
    </StackLayout>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:scanner="clr-namespace:BarcodeScanning;assembly=BarcodeScanning.Native.Maui">
    <StackLayout>
        <scanner:CameraView x:Name="CameraView"
                            OnDetectionFinished="OnBarcodeDetected"
                            CameraEnabled="True"
                            BarcodeFormats="All"
                            VerticalOptions="FillAndExpand" />
        <Label x:Name="ResultLabel" Text="Waiting for scan..." />
    </StackLayout>
</ContentPage>
XML

条形码扫描.MAUI 方法 — 代码隐藏:

using BarcodeScanning;

private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode != null)
        MainThread.BeginInvokeOnMainThread(() =>
            ResultLabel.Text = barcode.DisplayValue);
}
using BarcodeScanning;

private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode != null)
        MainThread.BeginInvokeOnMainThread(() =>
            ResultLabel.Text = barcode.DisplayValue);
}
Imports BarcodeScanning

Private Sub OnBarcodeDetected(sender As Object, e As OnDetectionFinishedEventArg)
    Dim barcode = e.BarcodeResults.FirstOrDefault()
    If barcode IsNot Nothing Then
        MainThread.BeginInvokeOnMainThread(Sub()
                                               ResultLabel.Text = barcode.DisplayValue
                                           End Sub)
    End If
End Sub
$vbLabelText   $csharpLabel

IronBarcode方法 — XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
    <StackLayout>
        <Button Text="Scan Barcode" Clicked="ScanButton_Clicked" />
        <Label x:Name="ResultLabel" Text="Tap to scan..." />
    </StackLayout>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
    <StackLayout>
        <Button Text="Scan Barcode" Clicked="ScanButton_Clicked" />
        <Label x:Name="ResultLabel" Text="Tap to scan..." />
    </StackLayout>
</ContentPage>
XML

IronBarcode方法——代码隐藏:

using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var first = results.FirstOrDefault();
    ResultLabel.Text = first?.Value ?? "No barcode found";
}
using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var first = results.FirstOrDefault();
    ResultLabel.Text = first?.Value ?? "No barcode found";
}
Imports IronBarCode
Imports System.IO
Imports System.Linq

Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
    Dim photo = Await MediaPicker.CapturePhotoAsync()
    If photo Is Nothing Then Return

    Using stream = Await photo.OpenReadAsync()
        Using ms As New MemoryStream()
            Await stream.CopyToAsync(ms)

            Dim results = BarcodeReader.Read(ms.ToArray())
            Dim first = results.FirstOrDefault()
            ResultLabel.Text = If(first?.Value, "No barcode found")
        End Using
    End Using
End Sub
$vbLabelText   $csharpLabel

这段代码可以在 iOS、Android 和 Windows MAUI 上运行,无需任何平台特定的分支。 用户体验从应用内实时取景器变为平台的原生相机屏幕——适用于大多数商业应用。 IronBarcode MAUI 阅读指南涵盖了其他配置选项。

每次扫描处理多个条形码

条形码扫描.MAUI 方法:

private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    foreach (var barcode in e.BarcodeResults)
    {
        MainThread.BeginInvokeOnMainThread(() =>
            Console.WriteLine($"Found: {barcode.DisplayValue}"));
    }
}
private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    foreach (var barcode in e.BarcodeResults)
    {
        MainThread.BeginInvokeOnMainThread(() =>
            Console.WriteLine($"Found: {barcode.DisplayValue}"));
    }
}
Private Sub OnBarcodeDetected(sender As Object, e As OnDetectionFinishedEventArg)
    For Each barcode In e.BarcodeResults
        MainThread.BeginInvokeOnMainThread(Sub()
                                               Console.WriteLine($"Found: {barcode.DisplayValue}")
                                           End Sub)
    Next
End Sub
$vbLabelText   $csharpLabel

IronBarcode方法:

using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var options = new BarcodeReaderOptions
    {
        Speed = ReadingSpeed.Balanced,
        ExpectMultipleBarcodes = true
    };

    var results = BarcodeReader.Read(ms.ToArray(), options);
    foreach (var result in results)
        Console.WriteLine($"{result.Format}: {result.Value}");
}
using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var options = new BarcodeReaderOptions
    {
        Speed = ReadingSpeed.Balanced,
        ExpectMultipleBarcodes = true
    };

    var results = BarcodeReader.Read(ms.ToArray(), options);
    foreach (var result in results)
        Console.WriteLine($"{result.Format}: {result.Value}");
}
Imports IronBarCode
Imports System.IO

Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
    Dim photo = Await MediaPicker.CapturePhotoAsync()
    If photo Is Nothing Then Return

    Using stream = Await photo.OpenReadAsync()
        Using ms As New MemoryStream()
            Await stream.CopyToAsync(ms)

            Dim options As New BarcodeReaderOptions With {
                .Speed = ReadingSpeed.Balanced,
                .ExpectMultipleBarcodes = True
            }

            Dim results = BarcodeReader.Read(ms.ToArray(), options)
            For Each result In results
                Console.WriteLine($"{result.Format}: {result.Value}")
            Next
        End Using
    End Using
End Sub
$vbLabelText   $csharpLabel

ExpectMultipleBarcodes = true 告诉读者在找到第一个条形码后继续扫描。 如果没有这个选项,调用会在第一次匹配时返回,这对于单个条形码的情况来说速度更快。

iOS UPC-A 修复:移除标准化变通方案

如果你的代码库中有 UPC-A 前导零的变通方案,请将其完全删除。 IronBarcode无需任何人工干预即可返回正确的 12 位数值。

条形码扫描.MAUI 方法——已采取变通方案:

private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode == null) return;

    var value = barcode.DisplayValue;

    // Workaround: Apple Vision returns 13 digits for UPC-A
    if (barcode.BarcodeFormat == BarcodeFormats.Upca && value.Length == 13)
        value = value.Substring(1);

    ProcessBarcode(value, barcode.BarcodeFormat.ToString());
}
private void OnBarcodeDetected(object sender, OnDetectionFinishedEventArg e)
{
    var barcode = e.BarcodeResults.FirstOrDefault();
    if (barcode == null) return;

    var value = barcode.DisplayValue;

    // Workaround: Apple Vision returns 13 digits for UPC-A
    if (barcode.BarcodeFormat == BarcodeFormats.Upca && value.Length == 13)
        value = value.Substring(1);

    ProcessBarcode(value, barcode.BarcodeFormat.ToString());
}
Private Sub OnBarcodeDetected(sender As Object, e As OnDetectionFinishedEventArg)
    Dim barcode = e.BarcodeResults.FirstOrDefault()
    If barcode Is Nothing Then Return

    Dim value = barcode.DisplayValue

    ' Workaround: Apple Vision returns 13 digits for UPC-A
    If barcode.BarcodeFormat = BarcodeFormats.Upca AndAlso value.Length = 13 Then
        value = value.Substring(1)
    End If

    ProcessBarcode(value, barcode.BarcodeFormat.ToString())
End Sub
$vbLabelText   $csharpLabel

IronBarcode方案——无需任何变通方法:

using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var first = results.FirstOrDefault();
    if (first == null) return;

    // result.Value is the correct 12-digit UPC-A — no normalization needed
    ProcessBarcode(first.Value, first.Format.ToString());
}
using IronBarCode;

private async void ScanButton_Clicked(object sender, EventArgs e)
{
    var photo = await MediaPicker.CapturePhotoAsync();
    if (photo == null) return;

    using var stream = await photo.OpenReadAsync();
    using var ms = new MemoryStream();
    await stream.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var first = results.FirstOrDefault();
    if (first == null) return;

    // result.Value is the correct 12-digit UPC-A — no normalization needed
    ProcessBarcode(first.Value, first.Format.ToString());
}
Imports IronBarCode
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks

Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
    Dim photo = Await MediaPicker.CapturePhotoAsync()
    If photo Is Nothing Then Return

    Using stream = Await photo.OpenReadAsync()
        Using ms As New MemoryStream()
            Await stream.CopyToAsync(ms)

            Dim results = BarcodeReader.Read(ms.ToArray())
            Dim first = results.FirstOrDefault()
            If first Is Nothing Then Return

            ' result.Value is the correct 12-digit UPC-A — no normalization needed
            ProcessBarcode(first.Value, first.Format.ToString())
        End Using
    End Using
End Sub
$vbLabelText   $csharpLabel

删除与Substring(1)的任何匹配项 — 该代码在迁移后不起作用。

添加文件和 PDF 支持

BarcodeScanning.Native.Maui 没有用于文件或 PDF 输入的等效功能。 如果这是在迁移时需要满足的新要求:

条形码扫描.MAUI 方法:

// 否 equivalent exists — BarcodeScanning.Native.Maui cannot read from files or PDFs
// 否 equivalent exists — BarcodeScanning.Native.Maui cannot read from files or PDFs
' 否 equivalent exists — BarcodeScanning.Native.Maui cannot read from files or PDFs
$vbLabelText   $csharpLabel

IronBarcode方法:

using IronBarCode;

// Read from a file the user picked with FilePicker
private async void ReadFileButton_Clicked(object sender, EventArgs e)
{
    var file = await FilePicker.PickAsync(new PickOptions
    {
        PickerTitle = "Select image or PDF"
    });
    if (file == null) return;

    var results = BarcodeReader.Read(file.FullPath);
    foreach (var result in results)
        ResultLabel.Text += $"\n{result.Format}: {result.Value}";
}

// Read barcodes from a PDF directly — no intermediate image step
private void ReadPdfBarcodes()
{
    var results = BarcodeReader.Read("shipment-manifest.pdf");
    foreach (var result in results)
        Console.WriteLine($"{result.Format}: {result.Value}");
}
using IronBarCode;

// Read from a file the user picked with FilePicker
private async void ReadFileButton_Clicked(object sender, EventArgs e)
{
    var file = await FilePicker.PickAsync(new PickOptions
    {
        PickerTitle = "Select image or PDF"
    });
    if (file == null) return;

    var results = BarcodeReader.Read(file.FullPath);
    foreach (var result in results)
        ResultLabel.Text += $"\n{result.Format}: {result.Value}";
}

// Read barcodes from a PDF directly — no intermediate image step
private void ReadPdfBarcodes()
{
    var results = BarcodeReader.Read("shipment-manifest.pdf");
    foreach (var result in results)
        Console.WriteLine($"{result.Format}: {result.Value}");
}
Imports IronBarCode

' Read from a file the user picked with FilePicker
Private Async Sub ReadFileButton_Clicked(sender As Object, e As EventArgs)
    Dim file = Await FilePicker.PickAsync(New PickOptions With {
        .PickerTitle = "Select image or PDF"
    })
    If file Is Nothing Then Return

    Dim results = BarcodeReader.Read(file.FullPath)
    For Each result In results
        ResultLabel.Text &= vbCrLf & $"{result.Format}: {result.Value}"
    Next
End Sub

' Read barcodes from a PDF directly — no intermediate image step
Private Sub ReadPdfBarcodes()
    Dim results = BarcodeReader.Read("shipment-manifest.pdf")
    For Each result In results
        Console.WriteLine($"{result.Format}: {result.Value}")
    Next
End Sub
$vbLabelText   $csharpLabel

IronBarcode PDF 阅读文档涵盖多页 PDF 支持和页面范围选择。

服务器端条形码处理

如果您的应用程序有需要条形码处理的后端ASP.NET API,同样的BarcodeReader.Read()调用可以在不修改的情况下运行。 BarcodeScanning.Native.Maui 没有服务器端对应的功能。

条形码扫描.MAUI 方法:

// 否 equivalent exists — BarcodeScanning.Native.Maui is a camera UI control
// and cannot run in a server process
// 否 equivalent exists — BarcodeScanning.Native.Maui is a camera UI control
// and cannot run in a server process
' 否 equivalent exists — BarcodeScanning.Native.Maui is a camera UI control
' and cannot run in a server process
$vbLabelText   $csharpLabel

IronBarcode方法:

using IronBarCode;

// ASP.NET endpoint — reads barcodes from an uploaded file
[HttpPost("scan")]
public async Task<IActionResult> ScanBarcode(IFormFile file)
{
    using var ms = new MemoryStream();
    await file.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var values = results.Select(r => new { r.Value, Format = r.Format.ToString() });
    return Ok(values);
}
using IronBarCode;

// ASP.NET endpoint — reads barcodes from an uploaded file
[HttpPost("scan")]
public async Task<IActionResult> ScanBarcode(IFormFile file)
{
    using var ms = new MemoryStream();
    await file.CopyToAsync(ms);

    var results = BarcodeReader.Read(ms.ToArray());
    var values = results.Select(r => new { r.Value, Format = r.Format.ToString() });
    return Ok(values);
}
Imports IronBarCode
Imports Microsoft.AspNetCore.Mvc
Imports System.IO
Imports System.Threading.Tasks

' ASP.NET endpoint — reads barcodes from an uploaded file
<HttpPost("scan")>
Public Async Function ScanBarcode(file As IFormFile) As Task(Of IActionResult)
    Using ms As New MemoryStream()
        Await file.CopyToAsync(ms)

        Dim results = BarcodeReader.Read(ms.ToArray())
        Dim values = results.Select(Function(r) New With {Key .Value = r.Value, Key .Format = r.Format.ToString()})
        Return Ok(values)
    End Using
End Function
$vbLabelText   $csharpLabel

相同的软件包、相同的 API、相同的行为——在移动端和服务器端。

生成条形码

BarcodeScanning.Native.Maui 没有生成 API。 IronBarcode可生成多种格式。

条形码扫描.MAUI 方法:

// 否 equivalent exists — BarcodeScanning.Native.Maui cannot generate barcodes
// 否 equivalent exists — BarcodeScanning.Native.Maui cannot generate barcodes
' 否 equivalent exists — BarcodeScanning.Native.Maui cannot generate barcodes
$vbLabelText   $csharpLabel

IronBarcode方法:

using IronBarCode;

// QR code
QRCodeWriter.CreateQrCode("https://example.com/product/12345", 500)
    .SaveAsPng("qr.png");

// Code128 barcode sized to specific dimensions
BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128)
    .ResizeTo(400, 100)
    .SaveAsPng("label.png");

// Get bytes for returning from an API or storing in a database
byte[] barcodeBytes = BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128)
    .ToPngBinaryData();
using IronBarCode;

// QR code
QRCodeWriter.CreateQrCode("https://example.com/product/12345", 500)
    .SaveAsPng("qr.png");

// Code128 barcode sized to specific dimensions
BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128)
    .ResizeTo(400, 100)
    .SaveAsPng("label.png");

// Get bytes for returning from an API or storing in a database
byte[] barcodeBytes = BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128)
    .ToPngBinaryData();
Imports IronBarCode

' QR code
QRCodeWriter.CreateQrCode("https://example.com/product/12345", 500) _
    .SaveAsPng("qr.png")

' Code128 barcode sized to specific dimensions
BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128) _
    .ResizeTo(400, 100) _
    .SaveAsPng("label.png")

' Get bytes for returning from an API or storing in a database
Dim barcodeBytes As Byte() = BarcodeWriter.CreateBarcode("ITEM-98765", BarcodeEncoding.Code128) _
    .ToPngBinaryData()
$vbLabelText   $csharpLabel

IronBarcode生成文档涵盖了所有支持的格式和样式选项。

条形码扫描.MAUI API 到IronBarcode映射参考

BarcodeScanning.Native.Maui IronBarcode
CameraView XAML 控件 移除 — 使用 Button + MediaPicker.CapturePhotoAsync()
OnDetectionFinished 事件 BarcodeReader.Read(imageBytes) 返回值
OnDetectionFinishedEventArg e BarcodeReader.Read() 的 IEnumerable 结果
e.BarcodeResults BarcodeReader.Read() 的返回值
e.BarcodeResults.FirstOrDefault() results.FirstOrDefault()
barcode.DisplayValue result.Value
barcode.BarcodeFormat result.Format
BarcodeFormats="All" 自动检测——无需配置
CameraEnabled="True" await MediaPicker.CapturePhotoAsync()
仅限 iOS 和 Android iOS、Android、Windows、macOS、MAUI
无需输入文件 BarcodeReader.Read(filePath)
无 PDF 输入 BarcodeReader.Read("document.pdf")
没有一代 BarcodeWriter.CreateBarcode() / QRCodeWriter.CreateQrCode()
iOS UPC-A 返回 13 位数字 返回正确的 12 位数字——无需归一化
PDF417 不可靠 支持

常见迁移问题和解决方案

问题一:实时取景器体验丢失

BarcodeScanning.MAUI: CameraView 控件将实时相机预览直接嵌入到MAUI页面中。 用户可以看到摄像头画面并对准条形码——无需按任何按钮即可自动检测。

方案: MediaPicker.CapturePhotoAsync() 显示平台相机屏幕。 对于大多数业务流程而言,这是可以接受的。 对于需要连续实时预览的消费类应用,摄像头帧可以直接传递给BarcodeReader.Read()

using IronBarCode;

// Continuous scanning: pass camera frame bytes to BarcodeReader.Read()
// (frame capture depends on your MAUI camera frame source)
private void ProcessCameraFrame(byte[] frameBytes)
{
    var results = BarcodeReader.Read(frameBytes);
    if (results.Any())
    {
        var first = results.First();
        MainThread.BeginInvokeOnMainThread(() =>
            ResultLabel.Text = first.Value);
    }
}
using IronBarCode;

// Continuous scanning: pass camera frame bytes to BarcodeReader.Read()
// (frame capture depends on your MAUI camera frame source)
private void ProcessCameraFrame(byte[] frameBytes)
{
    var results = BarcodeReader.Read(frameBytes);
    if (results.Any())
    {
        var first = results.First();
        MainThread.BeginInvokeOnMainThread(() =>
            ResultLabel.Text = first.Value);
    }
}
Imports IronBarCode

' Continuous scanning: pass camera frame bytes to BarcodeReader.Read()
' (frame capture depends on your MAUI camera frame source)
Private Sub ProcessCameraFrame(frameBytes As Byte())
    Dim results = BarcodeReader.Read(frameBytes)
    If results.Any() Then
        Dim first = results.First()
        MainThread.BeginInvokeOnMainThread(Sub()
                                               ResultLabel.Text = first.Value
                                           End Sub)
    End If
End Sub
$vbLabelText   $csharpLabel

这需要将相机帧源与IronBarcode分开连接。 在采取此方法之前,请评估是否真的需要实时预览,或者系统摄像头用户界面是否足够。

问题 2:e.BarcodeResults 属性名称和枚举值变更

BarcodeScanning.MAUI: barcode.DisplayValue 返回解码后的字符串; barcode.BarcodeFormat 返回一个 BarcodeScanning 库的枚举值。

方案:barcode.BarcodeFormat。 迭代模式相同:

// Before
foreach (var barcode in e.BarcodeResults)
    Console.WriteLine(barcode.DisplayValue);

// After
var results = BarcodeReader.Read(imageBytes);
foreach (var result in results)
    Console.WriteLine(result.Value);
// Before
foreach (var barcode in e.BarcodeResults)
    Console.WriteLine(barcode.DisplayValue);

// After
var results = BarcodeReader.Read(imageBytes);
foreach (var result in results)
    Console.WriteLine(result.Value);
' Before
For Each barcode In e.BarcodeResults
    Console.WriteLine(barcode.DisplayValue)
Next

' After
Dim results = BarcodeReader.Read(imageBytes)
For Each result In results
    Console.WriteLine(result.Value)
Next
$vbLabelText   $csharpLabel

问题 3:用于 UI 更新的线程封送

BarcodeScanning.MAUI: OnDetectionFinished 在后台线程触发,因此所有UI更新都需要MainThread.BeginInvokeOnMainThread().

方案: 使用MediaPicker + await返回之后的继续会出现在调用上下文中 — 通常是主线程。 围绕结果显示的MainThread.BeginInvokeOnMainThread()包装器通常可以移除,从而简化处理代码。

问题 4:MAUI 相机权限

BarcodeScanning.MAUI: 包会在设置中自动将相机权限添加到Info.plist中。

方案: 使用IronBarcode的MediaPicker,标准的MAUI相机权限必须手动存在。 这些是任何MAUI应用程序用于MediaPicker.CapturePhotoAsync()所需的相同权限,通常已经存在。 在设备上测试之前,确认Info.plist中设置。

条形码扫描.MAUI 迁移清单

迁移前任务

在进行任何更改之前,请运行以下搜索以查找所有 BarcodeScanning.Native.Maui 的使用情况:

grep -rn "using BarcodeScanning" --include="*.cs" .
grep -rn "using BarcodeScanning" --include="*.xaml" .
grep -rn "CameraView" --include="*.cs" .
grep -rn "CameraView" --include="*.xaml" .
grep -rn "OnDetectionFinished" --include="*.cs" .
grep -rn "OnDetectionFinishedEventArg" --include="*.cs" .
grep -rn "e\.BarcodeResults" --include="*.cs" .
grep -rn "DisplayValue" --include="*.cs" .
grep -rn "BarcodeFormats\.Upca" --include="*.cs" .
grep -rn "scanner:" --include="*.xaml" .
grep -rn "using BarcodeScanning" --include="*.cs" .
grep -rn "using BarcodeScanning" --include="*.xaml" .
grep -rn "CameraView" --include="*.cs" .
grep -rn "CameraView" --include="*.xaml" .
grep -rn "OnDetectionFinished" --include="*.cs" .
grep -rn "OnDetectionFinishedEventArg" --include="*.cs" .
grep -rn "e\.BarcodeResults" --include="*.cs" .
grep -rn "DisplayValue" --include="*.cs" .
grep -rn "BarcodeFormats\.Upca" --include="*.cs" .
grep -rn "scanner:" --include="*.xaml" .
SHELL

记录每一次命中。 注意哪些文件包含CameraView XAML使用(需要XAML更改),哪些只包含代码隐藏更改。 确定迁移后必须删除的任何 UPC-A 规范化变通方案。

代码更新任务

  1. 移除BarcodeScanning.Native.Maui NuGet 包
  2. 安装IronBarcode NuGet 包
  3. IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
  4. 在所有using BarcodeScanning;
  5. 从所有XAML文件中移除xmlns:scanner="..."命名空间声明
  6. 在XAML中用触发scanner:CameraView控件
  7. 从XAML中移除OnDetectionFinished="..."事件连接
  8. 使用async按钮点击处理程序
  9. 全面用barcode.DisplayValue
  10. 全面用barcode.BarcodeFormat
  11. 删除所有BarcodeFormats.Upca + Substring(1)规范化变通方案
  12. 在之前依赖BarcodeReaderOptions
  13. 在结果显示代码中移除MainThread.BeginInvokeOnMainThread()包装器,异步模式使其不再需要
  14. 验证Info.plist相机权限是否存在

迁移后测试

  • 验证 iOS 条形码扫描功能是否正常,以及 UPC-A 值是否以不带前导零的 12 位字符串形式返回。
  • 验证 Android 条形码扫描功能能否针对应用程序中使用的所有格式生成正确的值。
  • 验证 Windows MAUI 条形码扫描功能是否正常工作(如果 Windows 是构建目标)。
  • 如果使用真实的货运标签、驾驶执照或登机牌,请使用这些文件测试 PDF417 扫描功能。
  • 测试使用ExpectMultipleBarcodes = true的多条形码场景,并确认图像中的所有条形码都已返回
  • 验证文件选择器扫描(BarcodeReader.Read(filePath))在所有MAUI目标上是否工作
  • 如果 PDF 条形码读取是迁移过程中新增的功能,请验证其是否能够读取。
  • 确认如果添加了后端组件,服务器端BarcodeReader.Read()是否产生正确的结果
  • 运行所有现有的自动化测试,并将条形码值输出与迁移前的基线进行比较。

迁移到IronBarcode的主要优势

完全支持 Windows MAUI: IronBarcode可在所有四个 MAUI 目标平台(iOS、Android、Windows 和 macOS)上运行,使用相同的代码和相同的软件包。 适用于Windows的平台特定条形码实现是不需要的,应用程序代码中也无需#if WINDOWS块。

任何输入源: BarcodeReader.Read() 接受文件路径、字节数组、流和PDF文档。 任何条形码应用场景——相机拍摄、文件上传、图库图像、服务器端 PDF 处理——都使用相同的静态方法,并具有相同的结果类型。

正确的 UPC-A 值: IronBarcode在 iOS 上无需任何标准化代码即可返回正确的 12 位 UPC-A 值。 由于 BarcodeScanning.Native.Maui 的行为,历史 UPC-A 数据以前导零存储,但这不会影响迁移后读取值的准确性。

可靠的 PDF417:完全支持 PDF417,读取可靠。 货运标签、驾驶执照和登机牌均可扫描,而不会出现 BarcodeScanning.Native.Maui 的GitHub问题中记录的"大多数扫描从未发生"的限制。

条形码生成: BarcodeWriter.CreateBarcode()QRCodeWriter.CreateQrCode()生成Code128、QR、DataMatrix等格式为PNG文件或字节数组。 生成和读取功能均来自同一个软件包,无需额外依赖项。

服务器端部署: 同样的BarcodeReader.Read()调用在ASP.NET、Azure Functions、Docker容器和AWS Lambda中运行。 移动端和服务器端的条形码逻辑可以共享相同的 API、相同的格式支持和相同的结果类型,而无需维护两个独立的条形码实现。

常见问题解答

我为什么要从 BarcodeScanning.MAUI 迁移到 IronBarcode?

常见原因包括简化许可(消除 SDK + 运行时密钥的复杂性)、消除吞吐量限制、获得原生 PDF 支持、改进 Docker/CI/CD 部署以及减少生产代码中的 API 样板代码。

如何用 IronBarcode 替换 BarcodeScanning.MAUI API 调用?

用 `IronBarCode.License.LicenseKey = "key"` 替换实例创建和许可相关的样板代码。用 `BarcodeReader.Read(path)` 替换读取器调用,用 `BarcodeWriter.CreateBarcode(data, encoding)` 替换写入器调用。静态方法无需实例管理。

从 BarcodeScanning.MAUI 迁移到 IronBarcode 时,代码需要做多少更改?

大多数迁移都能减少代码行数。许可样板代码、实例构造函数和显式格式配置都被移除。核心读/写操作映射到更简洁的 IronBarcode 等效代码,并生成更清晰的结果对象。

迁移过程中是否需要同时安装 BarcodeScanning.MAUI 和 IronBarcode?

不。大多数迁移都是直接替换,而不是并行操作。一次迁移一个服务类,替换 NuGet 引用,并更新实例化和 API 调用模式,然后再迁移下一个类。

IronBarcode 的 NuGet 包名称是什么?

该软件包名为“IronBarCode”(B 和 C 大写)。使用“Install-Package IronBarCode”或“dotnet add package IronBarCode”进行安装。代码中的 using 指令为“using IronBarCode;”。

与 BarcodeScanning.MAUI 相比,IronBarcode 如何简化 Docker 部署?

IronBarcode 是一个 NuGet 包,不包含任何外部 SDK 文件或已挂载的许可证配置。在 Docker 环境中,设置 IRONBARCODE_LICENSE_KEY 环境变量后,该包会在启动时自动处理许可证验证。

从 BarcodeScanning.MAUI 迁移后,IronBarcode 是否能自动检测所有条形码格式?

是的。IronBarcode 可以自动检测所有支持格式的条码符号,无需显式枚举 BarcodeTypes。如果已知条码格式且性能至关重要,BarcodeReaderOptions 允许缩小搜索范围以进行优化。

IronBarcode 能否在不使用单独库的情况下读取 PDF 中的条形码?

是的。`BarcodeReader.Read("document.pdf")` 可以直接处理 PDF 文件。结果包括每个条形码的页码、格式、值和置信度。无需外部 PDF 渲染步骤。

IronBarcode如何处理并行条码处理?

IronBarcode 的静态方法是无状态且线程安全的。可以直接对文件列表使用 Parallel.ForEach,无需进行线程级实例管理。BarcodeReaderOptions.MaxParallelThreads 控制内部线程预算。

从 BarcodeScanning.MAUI 迁移到 IronBarcode 时,结果属性会发生哪些变化?

常见重命名:BarcodeValue 变为 Value,BarcodeType 变为 Format。IronBarcode 结果还会添加 Confidence 和 PageNumber。解决方案范围内的查找替换功能会处理现有结果处理代码中的重命名。

如何在 CI/CD 流水线中设置 IronBarcode 许可?

将 IRONBARCODE_LICENSE_KEY 存储为管道密钥,并在应用程序启动代码中赋值 IronBarCode.License.LicenseKey。一个密钥即可覆盖所有环境,包括开发、测试、预发布和生产环境。

IronBarcode是否支持生成自定义样式的二维码?

是的。QRCodeWriter.CreateQrCode() 支持通过 ChangeBarCodeColor() 自定义颜色、通过 AddBrandLogo() 嵌入徽标、可配置纠错级别以及多种输出格式,包括 PNG、JPG、PDF 和流媒体。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我