如何从ASP.NET Core中的PDF文件读取数据
手动生成单据和标签的问题
在仓库中,每天处理500个订单,并分两班次进行,拣货、包装和发货周期不能容许有摩擦。 从OMS屏幕上一一打印包装单,手动将订单详情复制到标签模板中,或者依赖于与.NET后端无法顺利集成的标签软件,每个订单的处理都多消耗了几秒,到了班次结束时,实际上会导致吞吐量的下降。
随着时间成本的累积,错误风险也在增加。当发货地址是手动转录而不是直接从订单记录中提取时,发货失误就会发生。 由于地址错误而导致的退货,花费的人工费和运费比大多数单个订单的利润还要高。 承运商提供的标签工具解决了地址问题,但不能生成仓库合并装箱前需要用来验证箱内物品的详细包装单,这些仍然需要从不同系统单独打印。
批量打印是大多数方法完全无法应对的地方。 操作经理在释放200个订单的波次时,需要在班次开始时准备好这些包装单,而不是通过一个新的web界面逐个订单地生成打印对话框。 格式需要适用于地板上的所有打印机,无论是激光、喷墨还是热敏的,而无须在仓库工作站上安装专有驱动程序。
场景是一致的:一个直接面向消费者的品牌每天打包数百个订单;一个生成托盘货物多行单据的B2B分销商;一个为装配线工人打印项目检查清单的订阅箱服务商;一个3PL提供商代表多个商家客户生成带有不同商标和退货地址的品牌单据。
解决方案:通过IronPDF库直接从订单数据生成可打印PDF文件
IronPDF使.NET OMS可以直接从订单数据生成可打印的PDF包装单和标签。 当订单进入"准备包装"阶段时,应用程序提取订单明细、发货地址、承运信息和特殊说明,填充到HTML模板中,该模板适用于目标纸张格式,ChromePdfRenderer生成PDF。
仓库人员可在任何标准或热敏打印机上打印输出,每个订单一份文件,或者在单个打印作业中批量打印覆盖整个拣货波次的多页PDF。 无需安装专有的标签软件、无需手动数据输入步骤,也没有按标签API费用。 渲染在现有的.NET应用程序内部作为一个单独的NuGet包运行,无需外部进程。
实际应用示例
1. 订单进入包装队列
订单在付款确认后自动转换为"准备包装",或当仓库主管从OMS发布拣货波次时。 应用程序查询批次中的每个订单数据库,包含SKU、产品名称和数量的订单行项目; 发货地址; 承运商和服务级别; 可用时预先分配的追踪编号; 以及任何特殊处理说明或礼物信息。
波次中的每个订单都从相同的HTML模板生成自己的PDF,并填入其特定数据。 对于200个订单的波次,这意味着进行200次渲染——每次只需毫秒——然后将结果合并为一个用于打印作业的文件。
2. 模板支持两种格式
包装单模板(信纸或A4)包含一个用于包装工核对每个项的勾选框的订单行项目表、订单号、收货地址和一个可扫描的订单ID。 标签模板的目标尺寸为4×6英寸(标准热敏标签尺寸),包含收货地址和退货地址、承运商条形码区域和发货重量。
这两个模板都是HTML和CSS文件,由应用程序团队所有。 更新包装单以添加新列、更改3PL单据上的商家标志、或调整新热敏打印机型号的标签边距,都是模板文件的更改,无需重新编译,无需重新部署业务逻辑。
示例HTML文件模板

3. 批量渲染并合并为一个打印作业
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
string slipTemplate = await File.ReadAllTextAsync("Templates/packing-slip.html");
var slipPdfs = new List<PdfDocument>();
foreach (var order in pickWave)
{
string lineItemRows = string.Concat(order.Items.Select(i =>
$"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"));
string html = slipTemplate
.Replace("{{OrderNumber}}", order.OrderNumber)
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted)
.Replace("{{LineItems}}", lineItemRows)
.Replace("{{SpecialInstructions}}", order.SpecialInstructions ?? "None");
slipPdfs.Add(renderer.RenderHtmlAsPdf(html));
}
PdfDocument waveBatch = PdfDocument.Merge(slipPdfs);
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
string slipTemplate = await File.ReadAllTextAsync("Templates/packing-slip.html");
var slipPdfs = new List<PdfDocument>();
foreach (var order in pickWave)
{
string lineItemRows = string.Concat(order.Items.Select(i =>
$"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"));
string html = slipTemplate
.Replace("{{OrderNumber}}", order.OrderNumber)
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted)
.Replace("{{LineItems}}", lineItemRows)
.Replace("{{SpecialInstructions}}", order.SpecialInstructions ?? "None");
slipPdfs.Add(renderer.RenderHtmlAsPdf(html));
}
PdfDocument waveBatch = PdfDocument.Merge(slipPdfs);
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf");
Imports IronPdf
Imports System.IO
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 10
renderer.RenderingOptions.MarginBottom = 10
Dim slipTemplate As String = Await File.ReadAllTextAsync("Templates/packing-slip.html")
Dim slipPdfs As New List(Of PdfDocument)()
For Each order In pickWave
Dim lineItemRows As String = String.Concat(order.Items.Select(Function(i) $"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"))
Dim html As String = slipTemplate _
.Replace("{{OrderNumber}}", order.OrderNumber) _
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted) _
.Replace("{{LineItems}}", lineItemRows) _
.Replace("{{SpecialInstructions}}", If(order.SpecialInstructions, "None"))
slipPdfs.Add(renderer.RenderHtmlAsPdf(html))
Next
Dim waveBatch As PdfDocument = PdfDocument.Merge(slipPdfs)
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf")
示例输出PDF 文档
PdfDocument.Merge() 合并每张单独的单据为一个多页的PDF。 仓库团队将整个波次的打印作业发送出去,一次点击工作站,一次输出收集。
4. 标签尺寸通过渲染选项设置
using IronPdf;
var renderer = new ChromePdfRenderer();
// 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom;
renderer.RenderingOptions.CustomPaperWidth = 4;
renderer.RenderingOptions.CustomPaperHeight = 6;
renderer.RenderingOptions.MarginTop = 0;
renderer.RenderingOptions.MarginBottom = 0;
renderer.RenderingOptions.MarginLeft = 0;
renderer.RenderingOptions.MarginRight = 0;
string labelHtml = $@"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>";
PdfDocument label = renderer.RenderHtmlAsPdf(labelHtml);
label.SaveAs($"label-{order.OrderNumber}.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
// 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom;
renderer.RenderingOptions.CustomPaperWidth = 4;
renderer.RenderingOptions.CustomPaperHeight = 6;
renderer.RenderingOptions.MarginTop = 0;
renderer.RenderingOptions.MarginBottom = 0;
renderer.RenderingOptions.MarginLeft = 0;
renderer.RenderingOptions.MarginRight = 0;
string labelHtml = $@"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>";
PdfDocument label = renderer.RenderHtmlAsPdf(labelHtml);
label.SaveAs($"label-{order.OrderNumber}.pdf");
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
' 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom
renderer.RenderingOptions.CustomPaperWidth = 4
renderer.RenderingOptions.CustomPaperHeight = 6
renderer.RenderingOptions.MarginTop = 0
renderer.RenderingOptions.MarginBottom = 0
renderer.RenderingOptions.MarginLeft = 0
renderer.RenderingOptions.MarginRight = 0
Dim labelHtml As String = $"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>"
Dim label As PdfDocument = renderer.RenderHtmlAsPdf(labelHtml)
label.SaveAs($"label-{order.OrderNumber}.pdf")
示例输出PDF文件和运单
零边距允许CSS控制到标签边缘的所有间距,这对承运商条形码区域必须达到纸张边界几毫米以内的热敏标签格式至关重要。
实际好处
批量效率。将整个拣货波次合并为一个PDF,意味着一次打印作业而不是几百次。 当仓库人员不再需要等待每个订单的打印对话框加载时,班次开始时间会缩短。
打印兼容性。 PDF在激光、喷墨和热敏打印机上一致呈现,无需在仓库工作站上安装专有驱动程序或标签软件。 OMS生成的格式是地板上每个打印机都可以接受的格式。
准确性。订单行项目和地址直接从订单记录中提取。 没有手动转录步骤,也没有错误输入地址或SKU到包装人员手中的可能。
灵活格式。 通过更换HTML模板和页面尺寸选项,相同的生成管道可以生成信纸大小的包装单和4×6的标签。 无需单独的工具,也无需单独的集成。
多租户品牌。 3PL提供商在模板路径中填充一个merchantId变量并加载商家专用的HTML文件,每位客户的单据都通过相同的管道携带其自己的标志、退货地址和品牌颜色,且无需额外的逻辑。
无单独标签费用。 渲染在进程中进行。 没有第三方标签API被调用,没有使用计量,也没有使高容量班次比低容量班次更昂贵的价格模型。
结语
拣货、包装和发货周期是一个吞吐量问题。 在订单进入包装队列和贴有标签的箱子离开码头之间,每个手动步骤都是一个约束,而生成包装单和标签是可以在OMS内部自动化的直接操作。
每种格式一个HTML模板,每个订单一个渲染调用,每个波次一个合并的PDF,每班次一个打印作业。 IronPDF在C#中处理PDF生成的整个生命周期,从ironpdf.com的HTML模板渲染到合并、保存和流式文档。 如果您准备好用您自己的订单数据和打印设置测试管道,请开始您的30天免费试用,并在本周内实现一个工作的批量单据生成器。




