使用IRONPDF 如何用 C# 将 PDF 转换为字节数组 Curtis Chau 已更新:2026年1月21日 下载 IronPDF NuGet 下载 DLL 下载 Windows 安装程序 免费试用 法学硕士副本 法学硕士副本 将页面复制为 Markdown 格式,用于 LLMs 在 ChatGPT 中打开 向 ChatGPT 咨询此页面 在双子座打开 向 Gemini 询问此页面 在 Grok 中打开 向 Grok 询问此页面 打开困惑 向 Perplexity 询问有关此页面的信息 分享 在 Facebook 上分享 分享到 X(Twitter) 在 LinkedIn 上分享 复制链接 电子邮件文章 IronPDF 提供了两种在 C# 中将 PDF 转换为字节数组的简单方法:使用BinaryData属性进行直接转换,或使用Stream属性进行更灵活的转换。 这样一来,无需编写复杂的代码即可实现高效的数据库存储、API 传输和内存文档操作。 将 PDF 文档转换为字节数组是现代 .NET 应用程序的基本要求。 无论是需要将PDF存储在数据库中,通过API传输文件,还是在内存中处理文档内容,理解字节数组转换都是必不可少的。 IronPDF通过其直观的 API 简化了这一过程,使您无需编写复杂的代码即可高效地转换文件。 什么是字节数组以及为什么要转换PDF文件? 字节数组是一种数据结构,将二进制数据存储为字节序列。 在处理PDF文件时,转换为字节数组具有几个优点。 这种格式能够高效地存储在数据库 BLOB 字段中,通过 Web 服务流畅传输,并简化内存中的文件内容操作。 在构建文档管理系统、实施云存储解决方案或创建处理 PDF 数据的 API 时,您经常会将 PDF 文件转换为字节数组。 二进制数据格式确保文档内容在传输和存储期间保持完整,保留所有页面、格式和嵌入资源。 这个过程类似于处理其他文件类型,如PNG图像或DOC文件。 了解更多关于在内存中处理 PDF 的信息。 何时应该使用字节数组转换? 在多种情况下,字节数组转换变得至关重要。 使用 BLOB 字段的数据库存储需要二进制格式。 处理文件上传的 API 端点通常将内容处理为字节数组。 云存储集成通常需要上传二进制数据。 当您需要在不进行磁盘 I/O 的情况下处理 PDF 文件时,基于内存的操作会更有优势。 在部署到Azure 环境时,字节数组处理对于无服务器函数来说尤为重要。 同样, AWS Lambda 部署也受益于内存高效的字节数组操作。 对于需要进行PDF 压缩的应用,使用字节数组可以直接访问优化例程。 实施SOC2 合规性的组织通常需要字节数组操作来实现安全的文档处理和加密工作流程。 对绩效有何影响? 使用 IronPDF 将 PDF 转换为字节数组对性能的影响极小。 BinaryData属性返回一个预先计算好的字节数组,因此它是一个 O(1) 操作。 内存使用量等于 PDF 文件大小加上最小开销。 对于大型文档,应考虑流式传输方式,避免同时将整个文件加载到内存中。 对于多线程 PDF 生成,字节数组操作可在处理阶段之间提供线程安全的数据传输。 Chrome渲染引擎能够高效地处理内存分配,即使处理复杂文档也能确保最佳性能。 在实现并行 PDF 处理时,字节数组能够实现并发操作之间的安全数据共享。 企业环境通常使用Docker 部署来改善容器化应用程序的内存使用情况。 如何在C#中将PDF转换为字节数组? IronPDF提供了两种简单的方法将PDF文档转换为字节数组。 BinaryData属性提供PDF字节表示的直接访问,而Stream属性返回一个新的MemoryStream以提供额外的灵活性。 using IronPdf; // Configure renderer with optimization settings var renderer = new ChromePdfRenderer(); renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print; renderer.RenderingOptions.PrintHtmlBackgrounds = true; // Create a new PDF document from HTML var pdf = renderer.RenderHtmlAsPdf("<h1>Sample Document</h1><p>This is test content.</p>"); // Method 1: Direct conversion to byte array byte[] pdfBytes = pdf.BinaryData; // Method 2: Using MemoryStream with additional processing using (var memoryStream = pdf.Stream) { // Optional: Apply compression before converting to bytes byte[] pdfBytesFromStream = memoryStream.ToArray(); } // Verify conversion and display size System.Console.WriteLine($"PDF size: {pdfBytes.Length} bytes"); // Optional: Convert to Base64 for text-safe transmission string base64Pdf = Convert.ToBase64String(pdfBytes); using IronPdf; // Configure renderer with optimization settings var renderer = new ChromePdfRenderer(); renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print; renderer.RenderingOptions.PrintHtmlBackgrounds = true; // Create a new PDF document from HTML var pdf = renderer.RenderHtmlAsPdf("<h1>Sample Document</h1><p>This is test content.</p>"); // Method 1: Direct conversion to byte array byte[] pdfBytes = pdf.BinaryData; // Method 2: Using MemoryStream with additional processing using (var memoryStream = pdf.Stream) { // Optional: Apply compression before converting to bytes byte[] pdfBytesFromStream = memoryStream.ToArray(); } // Verify conversion and display size System.Console.WriteLine($"PDF size: {pdfBytes.Length} bytes"); // Optional: Convert to Base64 for text-safe transmission string base64Pdf = Convert.ToBase64String(pdfBytes); Imports IronPdf ' Configure renderer with optimization settings Dim renderer As New ChromePdfRenderer() renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print renderer.RenderingOptions.PrintHtmlBackgrounds = True ' Create a new PDF document from HTML Dim pdf = renderer.RenderHtmlAsPdf("<h1>Sample Document</h1><p>This is test content.</p>") ' Method 1: Direct conversion to byte array Dim pdfBytes As Byte() = pdf.BinaryData ' Method 2: Using MemoryStream with additional processing Using memoryStream = pdf.Stream ' Optional: Apply compression before converting to bytes Dim pdfBytesFromStream As Byte() = memoryStream.ToArray() End Using ' Verify conversion and display size System.Console.WriteLine($"PDF size: {pdfBytes.Length} bytes") ' Optional: Convert to Base64 for text-safe transmission Dim base64Pdf As String = Convert.ToBase64String(pdfBytes) $vbLabelText $csharpLabel 此代码演示了两种转换方法,并提供了可用于生产环境的模式。 BinaryData属性提供了最直接的方法,瞬间返回字节数组表示。 对于需要流操作的情况,Stream属性提供一个MemoryStream实例,可以使用ToArray()方法将其转换为字节。 更多详情请查看PdfDocument API 参考文档。 渲染选项允许对转换过程进行微调。 对于特殊文档布局,可以考虑使用自定义纸张尺寸或自定义页边距。 对于需要符合 PDF/A 规范的受监管行业,请在字节数组转换之前配置相应的设置。 预期输出结果是什么? Visual Studio 调试控制台显示 IronTesting.exe 已成功执行,创建了一个 33,589 字节的 PDF 文件,退出代码为 0,并显示命令提示符,等待用户输入以关闭。 你应该选择哪种方法? 当您需要立即访问字节数组而无需额外处理时,请使用BinaryData 。 这种方法转换速度最快,内存占用最少。 当您需要在最终转换之前链接操作(例如压缩或加密)时,请选择Stream方法。 流式方法也更容易与需要流式输入的 API 集成。 对于水印操作,流式方法允许中间处理。 在实施PDF 清理时,字节数组为安全操作提供了一个干净的平台。 该选择还取决于您是否正在处理用于网页优化的线性化 PDF 。 安全团队通常更喜欢采用流式方法来实施自定义加密和编辑工作流程。 如何处理编码问题? IronPDF 会自动在内部处理编码,确保 PDF 二进制数据保持不变。 字节数组包含原始 PDF 数据,而不是文本编码,因此您无需担心字符编码问题。 通过网络传输文本时,请使用 base64 编码以确保传输安全。 对于包含国际语言和 UTF-8 内容的文档,IronPDF 可以正确保留所有字符编码。 该库支持字体管理,以确保在不同系统上正确显示。 使用Web 字体时,字节数组转换会保留所有嵌入的字体数据。 处理符合 HIPAA 标准的文件的医疗机构非常重视这种编码保存方式,因为它有助于维护文件的完整性。 如何将现有PDF文档转换为字节数组? 在计算机上处理现有PDF文件时,IronPDF可以轻松读取文件内容并将其转换为字节数组。 using IronPdf; using System.IO; using System; try { // Load an existing PDF document with error handling var existingPdf = PdfDocument.FromFile("report.pdf"); // Convert to byte array byte[] fileBytes = existingPdf.BinaryData; // Alternative: Using System.IO for direct file reading byte[] directBytes = File.ReadAllBytes("report.pdf"); // Create PdfDocument from byte array with validation var loadedPdf = new PdfDocument(directBytes); // Verify pages were loaded correctly int pageCount = loadedPdf.PageCount; System.Console.WriteLine($"Loaded PDF with {pageCount} pages"); // Additional validation: Check file structure if (loadedPdf.PageCount == 0) { throw new InvalidOperationException("PDF contains no pages"); } // Optional: Extract metadata for verification var metadata = loadedPdf.MetaData; Console.WriteLine($"Title: {metadata.Title}"); Console.WriteLine($"Author: {metadata.Author}"); } catch (Exception ex) { Console.WriteLine($"Error processing PDF: {ex.Message}"); // Implement appropriate error handling } using IronPdf; using System.IO; using System; try { // Load an existing PDF document with error handling var existingPdf = PdfDocument.FromFile("report.pdf"); // Convert to byte array byte[] fileBytes = existingPdf.BinaryData; // Alternative: Using System.IO for direct file reading byte[] directBytes = File.ReadAllBytes("report.pdf"); // Create PdfDocument from byte array with validation var loadedPdf = new PdfDocument(directBytes); // Verify pages were loaded correctly int pageCount = loadedPdf.PageCount; System.Console.WriteLine($"Loaded PDF with {pageCount} pages"); // Additional validation: Check file structure if (loadedPdf.PageCount == 0) { throw new InvalidOperationException("PDF contains no pages"); } // Optional: Extract metadata for verification var metadata = loadedPdf.MetaData; Console.WriteLine($"Title: {metadata.Title}"); Console.WriteLine($"Author: {metadata.Author}"); } catch (Exception ex) { Console.WriteLine($"Error processing PDF: {ex.Message}"); // Implement appropriate error handling } Imports IronPdf Imports System.IO Imports System Try ' Load an existing PDF document with error handling Dim existingPdf = PdfDocument.FromFile("report.pdf") ' Convert to byte array Dim fileBytes As Byte() = existingPdf.BinaryData ' Alternative: Using System.IO for direct file reading Dim directBytes As Byte() = File.ReadAllBytes("report.pdf") ' Create PdfDocument from byte array with validation Dim loadedPdf = New PdfDocument(directBytes) ' Verify pages were loaded correctly Dim pageCount As Integer = loadedPdf.PageCount Console.WriteLine($"Loaded PDF with {pageCount} pages") ' Additional validation: Check file structure If loadedPdf.PageCount = 0 Then Throw New InvalidOperationException("PDF contains no pages") End If ' Optional: Extract metadata for verification Dim metadata = loadedPdf.MetaData Console.WriteLine($"Title: {metadata.Title}") Console.WriteLine($"Author: {metadata.Author}") Catch ex As Exception Console.WriteLine($"Error processing PDF: {ex.Message}") ' Implement appropriate error handling End Try $vbLabelText $csharpLabel 上面的代码展示了两种处理现有文件并具有完整错误处理能力的方法。 IronPDF的FromFile方法加载文档并提供BinaryData属性的访问。 或者,你可以直接使用System.IO.File.ReadAllBytes()读入字节,然后从这些字节创建一个PdfDocument实例。 这种技术在处理文件路径或处理多个文档时很有用。 对于从已加载的 PDF 中提取文本和图像,字节数组格式提供了高效的访问方式。 在实现PDF 表单编辑时,字节数组可以实现表单数据的保留。 元数据管理功能与字节数组转换无缝协作。 金融机构经常使用这些技术来实现PDF/A 归档合规性和ZUGFeRD 电子发票。 ! Microsoft Visual Studio 调试控制台显示 PDF 文档已成功加载,并包含详细的页数统计(7 页)和元数据提取信息,表明在转换前已正确验证文件。 何时应该使用FromFile而不是ReadAllBytes ? 当您需要执行 PDF 特有的操作(例如页面操作、文本提取或添加注释)时,请使用PdfDocument.FromFile 。 此方法在加载过程中验证 PDF 结构。 使用File.ReadAllBytes可以实现简单的文件到数据库存储,无需验证,或者当您需要原始字节而无需 IronPDF 处理开销时。 FromFile 方法包含对损坏文件的内置验证。 对于拆分 PDF , FromFile可直接访问页面操作。 合并 PDF 文件时,经过验证的结构可确保兼容性。 企业系统通常使用批处理来高效地处理多个文档。 如何处理大型PDF文件? 对于超过 100MB 的 PDF 文件,请考虑分块处理以避免内存问题。 使用FileStream进行文件流式传输以实现逐步读取。 向用户显示大型文档时,应实现分页功能。 在批量操作期间,使用性能计数器监控内存使用情况。 PDF压缩功能有助于在转换前减小文件大小。 将PDF 栅格化为图像时,应逐页处理以管理内存。 对于大规模操作,可以考虑使用IronPDF 的性能优化技术。 对于大型文档,在AWS Lambda上进行云部署需要仔细配置内存。 关于错误处理? 将文件操作包装在 try-catch 块中,以处理FileNotFoundException和IOException 。 处理前请验证文件是否存在。 加载大文件前请检查可用内存。 对可能出现临时访问问题的网络存储文件实现重试逻辑。 对于调试 HTML 转 PDF 转换,正确的错误处理可以发现渲染问题。 处理大量 JavaScript 内容时,请捕获超时异常。 自定义日志记录功能有助于跟踪生产环境中的转换错误。 企业环境通常会与集中式日志系统集成,以进行合规性审计。 如何将字节数组转换回PDF? 将字节数组转换回PDF文档同样简单。 从数据库检索 PDF 数据或通过 API 接收文件时,此功能至关重要。 using IronPdf; using System; using System.Data.SqlClient; // Example: Retrieve from SQL Server database byte[] pdfBytes = GetPdfBytesFromDatabase(documentId: 123); // Create PdfDocument from byte array with validation var pdfDocument = new PdfDocument(pdfBytes); // Perform operations on the restored document // Add watermark pdfDocument.ApplyWatermark("<h2 style='color:red'>CONFIDENTIAL</h2>", opacity: 50, rotation: -45); // Add page numbers pdfDocument.AddTextHeaders("{page} of {total-pages}", IronPdf.Rendering.PdfCssMediaType.Print); // Save the modified PDF pdfDocument.SaveAs("modified-document.pdf"); // Or get updated bytes for storage byte[] updatedBytes = pdfDocument.BinaryData; // Store back to database SaveToDatabase(documentId: 123, pdfData: updatedBytes); // Example database retrieval method byte[] GetPdfBytesFromDatabase(int documentId) { using (var connection = new SqlConnection("YourConnectionString")) { var command = new SqlCommand( "SELECT PdfData FROM Documents WHERE Id = @id", connection); command.Parameters.AddWithValue("@id", documentId); connection.Open(); return (byte[])command.ExecuteScalar(); } } // Example database save method void SaveToDatabase(int documentId, byte[] pdfData) { using (var connection = new SqlConnection("YourConnectionString")) { var command = new SqlCommand( "UPDATE Documents SET PdfData = @data WHERE Id = @id", connection); command.Parameters.AddWithValue("@id", documentId); command.Parameters.AddWithValue("@data", pdfData); connection.Open(); command.ExecuteNonQuery(); } } using IronPdf; using System; using System.Data.SqlClient; // Example: Retrieve from SQL Server database byte[] pdfBytes = GetPdfBytesFromDatabase(documentId: 123); // Create PdfDocument from byte array with validation var pdfDocument = new PdfDocument(pdfBytes); // Perform operations on the restored document // Add watermark pdfDocument.ApplyWatermark("<h2 style='color:red'>CONFIDENTIAL</h2>", opacity: 50, rotation: -45); // Add page numbers pdfDocument.AddTextHeaders("{page} of {total-pages}", IronPdf.Rendering.PdfCssMediaType.Print); // Save the modified PDF pdfDocument.SaveAs("modified-document.pdf"); // Or get updated bytes for storage byte[] updatedBytes = pdfDocument.BinaryData; // Store back to database SaveToDatabase(documentId: 123, pdfData: updatedBytes); // Example database retrieval method byte[] GetPdfBytesFromDatabase(int documentId) { using (var connection = new SqlConnection("YourConnectionString")) { var command = new SqlCommand( "SELECT PdfData FROM Documents WHERE Id = @id", connection); command.Parameters.AddWithValue("@id", documentId); connection.Open(); return (byte[])command.ExecuteScalar(); } } // Example database save method void SaveToDatabase(int documentId, byte[] pdfData) { using (var connection = new SqlConnection("YourConnectionString")) { var command = new SqlCommand( "UPDATE Documents SET PdfData = @data WHERE Id = @id", connection); command.Parameters.AddWithValue("@id", documentId); command.Parameters.AddWithValue("@data", pdfData); connection.Open(); command.ExecuteNonQuery(); } } Imports IronPdf Imports System Imports System.Data.SqlClient ' Example: Retrieve from SQL Server database Dim pdfBytes As Byte() = GetPdfBytesFromDatabase(documentId:=123) ' Create PdfDocument from byte array with validation Dim pdfDocument As New PdfDocument(pdfBytes) ' Perform operations on the restored document ' Add watermark pdfDocument.ApplyWatermark("<h2 style='color:red'>CONFIDENTIAL</h2>", opacity:=50, rotation:=-45) ' Add page numbers pdfDocument.AddTextHeaders("{page} of {total-pages}", IronPdf.Rendering.PdfCssMediaType.Print) ' Save the modified PDF pdfDocument.SaveAs("modified-document.pdf") ' Or get updated bytes for storage Dim updatedBytes As Byte() = pdfDocument.BinaryData ' Store back to database SaveToDatabase(documentId:=123, pdfData:=updatedBytes) ' Example database retrieval method Function GetPdfBytesFromDatabase(documentId As Integer) As Byte() Using connection As New SqlConnection("YourConnectionString") Dim command As New SqlCommand("SELECT PdfData FROM Documents WHERE Id = @id", connection) command.Parameters.AddWithValue("@id", documentId) connection.Open() Return CType(command.ExecuteScalar(), Byte()) End Using End Function ' Example database save method Sub SaveToDatabase(documentId As Integer, pdfData As Byte()) Using connection As New SqlConnection("YourConnectionString") Dim command As New SqlCommand("UPDATE Documents SET PdfData = @data WHERE Id = @id", connection) command.Parameters.AddWithValue("@id", documentId) command.Parameters.AddWithValue("@data", pdfData) connection.Open() command.ExecuteNonQuery() End Using End Sub $vbLabelText $csharpLabel PdfDocument构造函数直接接受字节数组,从而能够将二进制数据平滑地转换回可用的 PDF。 当实现文档存储系统时,这种方法尤其有用,因为在这种系统中,PDF 文件以 BLOB 的形式存储在数据库中。 保存前,您可以添加新页面或修改现有内容。 添加页眉和页脚时,恢复后的文档保持了完整的功能。 在实现数字签名时,字节数组存储用于保存证书数据。 页码功能与数据库中存储的文档无缝协作。 医疗保健系统通常会将此与PDF/UA 合规性结合起来,以满足无障碍访问要求。 工作流程图展示了从数据库存储的字节数组到 PdfDocument 操作,再到最终输出修改后的 PDF 文件的整个 PDF 处理过程,完整呈现了数据转换流程。 如何验证PDF文件的完整性? IronPDF 在从字节数组创建文档时会自动验证 PDF 结构。 无效或损坏的数据会抛出PdfException 。 对关键文档实施校验和验证。 比较存储前后字节数组的长度,以检测截断。 使用 PDF/A 合规性检查来满足长期存档需求。 PDF/A 合规性功能确保文档的长期保存。 对于PDF/UA 可访问性,验证确认符合标准。 使用不同版本的 PDF 文件时,完整性检查会验证兼容性。 企业架构师通常会实施数字签名验证来验证文档的真实性。 常见的数据库存储模式有哪些? 在 SQL Server 中将 PDF 存储为 VARBINARY(MAX) 类型,在其他数据库中将 PDF 存储为 BLOB 类型。 为了提高查询效率,应单独索引元数据。 考虑使用压缩来优化存储,但要测试其对检索性能的影响。 通过存储多个带有时间戳的字节数组来实现版本控制,以记录文档历史记录。 对于Azure Blob 存储集成,字节数组是理想的格式。 在实现版本历史记录时,将每个版本存储为单独的字节数据。 元数据提取有助于构建与存储的 PDF 一起可搜索的索引。 金融机构通常会将此与审计跟踪结合起来,以满足合规性要求。## 如何处理内存流和文件内容? 内存流提供了一种无需创建临时文件即可管理 PDF 内容的有效方法。 这在需要动态生成和提供 PDF 的 Web 应用程序中尤其有利。 using IronPdf; using System.IO; using System.Threading.Tasks; public class PdfService { private readonly ChromePdfRenderer renderer; public PdfService() { renderer = new ChromePdfRenderer(); // Configure for optimal memory usage renderer.RenderingOptions.CreatePdfFormsFromHtml = true; renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print; renderer.RenderingOptions.EnableJavaScript = true; renderer.RenderingOptions.RenderDelay = 500; // Wait for JS } public async Task<byte[]> GenerateInvoicePdfAsync(InvoiceModel invoice) { // Generate HTML from template string html = GenerateInvoiceHtml(invoice); // Generate PDF in memory using (var memoryStream = new MemoryStream()) { // Create PDF with async rendering var pdf = await renderer.RenderHtmlAsPdfAsync(html); // Apply security settings pdf.SecuritySettings.UserPassword = invoice.CustomerEmail; pdf.SecuritySettings.OwnerPassword = "admin123"; pdf.SecuritySettings.AllowUserPrinting = true; pdf.SecuritySettings.AllowUserCopyPasteContent = false; // Save to stream pdf.SaveAs(memoryStream); // Convert stream to byte array byte[] pdfData = memoryStream.ToArray(); // Optional: Save to cache for future requests await CachePdfAsync(invoice.Id, pdfData); return pdfData; } } public async Task<PdfDocument> LoadAndModifyPdfAsync(byte[] storedBytes) { using (var memoryStream = new MemoryStream(storedBytes)) { var restoredPdf = new PdfDocument(memoryStream); // Add annotations restoredPdf.AddTextAnnotation("Review needed", pageIndex: 0, x: 100, y: 100); // Apply stamps restoredPdf.StampHtml("<img src='approved.png'/>", new HtmlStamp() { Width = 100, Height = 100 }); return restoredPdf; } } private string GenerateInvoiceHtml(InvoiceModel invoice) { // Template generation logic return $@" <html> <head> <link href='___PROTECTED_URL_74___ rel='stylesheet'> <style> body {{ font-family: 'Roboto', sans-serif; }} .invoice-header {{ background-color: #f0f0f0; padding: 20px; }} .total {{ font-weight: bold; font-size: 24px; color: #2e7d32; }} </style> </head> <body> <div class='invoice-header'> <h1>Invoice #{invoice.Id}</h1> <p>Date: {invoice.Date:yyyy-MM-dd}</p> </div> <p>Customer: {invoice.CustomerName}</p> <p class='total'>Total: ${invoice.Total:F2}</p> </body> </html>"; } private async Task CachePdfAsync(string invoiceId, byte[] pdfData) { // Cache implementation await Task.CompletedTask; } } // Usage example public async Task<IActionResult> DownloadInvoice(string invoiceId) { var service = new PdfService(); var invoice = GetInvoiceFromDatabase(invoiceId); byte[] pdfBytes = await service.GenerateInvoicePdfAsync(invoice); return File(pdfBytes, "application/pdf", $"Invoice-{invoiceId}.pdf"); } using IronPdf; using System.IO; using System.Threading.Tasks; public class PdfService { private readonly ChromePdfRenderer renderer; public PdfService() { renderer = new ChromePdfRenderer(); // Configure for optimal memory usage renderer.RenderingOptions.CreatePdfFormsFromHtml = true; renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print; renderer.RenderingOptions.EnableJavaScript = true; renderer.RenderingOptions.RenderDelay = 500; // Wait for JS } public async Task<byte[]> GenerateInvoicePdfAsync(InvoiceModel invoice) { // Generate HTML from template string html = GenerateInvoiceHtml(invoice); // Generate PDF in memory using (var memoryStream = new MemoryStream()) { // Create PDF with async rendering var pdf = await renderer.RenderHtmlAsPdfAsync(html); // Apply security settings pdf.SecuritySettings.UserPassword = invoice.CustomerEmail; pdf.SecuritySettings.OwnerPassword = "admin123"; pdf.SecuritySettings.AllowUserPrinting = true; pdf.SecuritySettings.AllowUserCopyPasteContent = false; // Save to stream pdf.SaveAs(memoryStream); // Convert stream to byte array byte[] pdfData = memoryStream.ToArray(); // Optional: Save to cache for future requests await CachePdfAsync(invoice.Id, pdfData); return pdfData; } } public async Task<PdfDocument> LoadAndModifyPdfAsync(byte[] storedBytes) { using (var memoryStream = new MemoryStream(storedBytes)) { var restoredPdf = new PdfDocument(memoryStream); // Add annotations restoredPdf.AddTextAnnotation("Review needed", pageIndex: 0, x: 100, y: 100); // Apply stamps restoredPdf.StampHtml("<img src='approved.png'/>", new HtmlStamp() { Width = 100, Height = 100 }); return restoredPdf; } } private string GenerateInvoiceHtml(InvoiceModel invoice) { // Template generation logic return $@" <html> <head> <link href='___PROTECTED_URL_74___ rel='stylesheet'> <style> body {{ font-family: 'Roboto', sans-serif; }} .invoice-header {{ background-color: #f0f0f0; padding: 20px; }} .total {{ font-weight: bold; font-size: 24px; color: #2e7d32; }} </style> </head> <body> <div class='invoice-header'> <h1>Invoice #{invoice.Id}</h1> <p>Date: {invoice.Date:yyyy-MM-dd}</p> </div> <p>Customer: {invoice.CustomerName}</p> <p class='total'>Total: ${invoice.Total:F2}</p> </body> </html>"; } private async Task CachePdfAsync(string invoiceId, byte[] pdfData) { // Cache implementation await Task.CompletedTask; } } // Usage example public async Task<IActionResult> DownloadInvoice(string invoiceId) { var service = new PdfService(); var invoice = GetInvoiceFromDatabase(invoiceId); byte[] pdfBytes = await service.GenerateInvoicePdfAsync(invoice); return File(pdfBytes, "application/pdf", $"Invoice-{invoiceId}.pdf"); } Imports IronPdf Imports System.IO Imports System.Threading.Tasks Public Class PdfService Private ReadOnly renderer As ChromePdfRenderer Public Sub New() renderer = New ChromePdfRenderer() ' Configure for optimal memory usage renderer.RenderingOptions.CreatePdfFormsFromHtml = True renderer.RenderingOptions.CssMediaType = PdfCssMediaType.Print renderer.RenderingOptions.EnableJavaScript = True renderer.RenderingOptions.RenderDelay = 500 ' Wait for JS End Sub Public Async Function GenerateInvoicePdfAsync(invoice As InvoiceModel) As Task(Of Byte()) ' Generate HTML from template Dim html As String = GenerateInvoiceHtml(invoice) ' Generate PDF in memory Using memoryStream As New MemoryStream() ' Create PDF with async rendering Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html) ' Apply security settings pdf.SecuritySettings.UserPassword = invoice.CustomerEmail pdf.SecuritySettings.OwnerPassword = "admin123" pdf.SecuritySettings.AllowUserPrinting = True pdf.SecuritySettings.AllowUserCopyPasteContent = False ' Save to stream pdf.SaveAs(memoryStream) ' Convert stream to byte array Dim pdfData As Byte() = memoryStream.ToArray() ' Optional: Save to cache for future requests Await CachePdfAsync(invoice.Id, pdfData) Return pdfData End Using End Function Public Async Function LoadAndModifyPdfAsync(storedBytes As Byte()) As Task(Of PdfDocument) Using memoryStream As New MemoryStream(storedBytes) Dim restoredPdf As New PdfDocument(memoryStream) ' Add annotations restoredPdf.AddTextAnnotation("Review needed", pageIndex:=0, x:=100, y:=100) ' Apply stamps restoredPdf.StampHtml("<img src='approved.png'/>", New HtmlStamp() With {.Width = 100, .Height = 100}) Return restoredPdf End Using End Function Private Function GenerateInvoiceHtml(invoice As InvoiceModel) As String ' Template generation logic Return $" <html> <head> <link href='___PROTECTED_URL_74___' rel='stylesheet'> <style> body {{ font-family: 'Roboto', sans-serif; }} .invoice-header {{ background-color: #f0f0f0; padding: 20px; }} .total {{ font-weight: bold; font-size: 24px; color: #2e7d32; }} </style> </head> <body> <div class='invoice-header'> <h1>Invoice #{invoice.Id}</h1> <p>Date: {invoice.Date:yyyy-MM-dd}</p> </div> <p>Customer: {invoice.CustomerName}</p> <p class='total'>Total: ${invoice.Total:F2}</p> </body> </html>" End Function Private Async Function CachePdfAsync(invoiceId As String, pdfData As Byte()) As Task ' Cache implementation Await Task.CompletedTask End Function End Class ' Usage example Public Async Function DownloadInvoice(invoiceId As String) As Task(Of IActionResult) Dim service As New PdfService() Dim invoice = GetInvoiceFromDatabase(invoiceId) Dim pdfBytes As Byte() = Await service.GenerateInvoicePdfAsync(invoice) Return File(pdfBytes, "application/pdf", $"Invoice-{invoiceId}.pdf") End Function $vbLabelText $csharpLabel 本示例演示了使用内存流创建、保存和加载 PDF 的完整过程,并提供了可用于生产环境的方法。 MemoryStream类充当 IronPDF 文档处理和 .NET 基于流的 API 之间的桥梁,从而实现高效的内存管理。 使用完毕后务必释放流,以释放资源。 了解更多关于将PDF导出到内存的信息。 对于HTML 到 PDF 的转换,内存流消除了磁盘 I/O 开销。 在实现CSS 媒体类型时,流允许动态应用样式。 JavaScript 渲染选项与基于内存的操作无缝集成。 企业应用程序通常使用背景层和前景层来实现品牌模板。 为什么使用MemoryStream而不是直接使用字节数组? MemoryStream为需要流导航的 API 提供可寻址访问。 它允许进行渐进式书写,而无需预先知道最终尺寸。 流接口与压缩库和加密操作集成得更好。 在最终输出之前,如果要将多个转换串联起来,请使用流。 异步 PDF 生成得益于基于流的处理。 对于自定义水印应用,流可以实现分层渲染。 在实现页面转换时,流提供了高效的中间存储。 处理HIPAA 文档的医疗保健系统通常需要基于流的加密工作流程。 如何提高内存使用效率? 在已知最终大小的情况下预先分配MemoryStream容量,以避免调整大小。 在高通量场景下使用RecyclableMemoryStream ,以降低 GC 压力。 使用 using 语句后立即释放流。 监控超过 85KB 的流的大对象堆 (LOH) 分配。 渲染延迟选项通过控制渲染时间来管理内存。 对于批量 PDF 操作,适当的流管理可以防止内存泄漏。 考虑对PDF文件进行扁平化处理以减少内存占用。 云部署通常可以利用IronPDF Slim 软件包来减少内存开销。 螺纹安全方面呢? MemoryStream操作默认情况下不是线程安全的。 每个线程使用单独的流实例,或者实现同步。 考虑ConcurrentQueue<byte[]>用于多线程字节数组处理。 避免在未进行适当锁定的情况下跨线程共享PdfDocument实例。 对于并发 PDF 生成,请将流操作按线程隔离。 多线程渲染示例展示了安全模式。 在实现异步操作时,请确保在所有代码路径中正确释放流。 企业系统通常使用消息队列模式进行分布式处理。 Web应用程序的最佳实践是什么? 在Web应用程序中提供PDF时,正确处理字节数组可确保最佳性能。 以下是在ASP.NET中向用户发送PDF字节的方法: using IronPdf; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; [ApiController] [Route("api/[controller]")] public class PdfController : ControllerBase { private readonly IMemoryCache _cache; private readonly ChromePdfRenderer _renderer; public PdfController(IMemoryCache cache) { _cache = cache; _renderer = new ChromePdfRenderer(); // Configure for web optimization _renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait; _renderer.RenderingOptions.MarginTop = 40; _renderer.RenderingOptions.MarginBottom = 40; _renderer.RenderingOptions.EnableJavaScript = true; _renderer.RenderingOptions.WaitFor.RenderDelay(200); } [HttpGet("report/{reportId}")] public async Task<IActionResult> GenerateReport(string reportId) { // Check cache first if (_cache.TryGetValue($"pdf_{reportId}", out byte[] cachedPdf)) { return File(cachedPdf, "application/pdf", $"report_{reportId}.pdf"); } try { // Generate report data var reportData = await GetReportDataAsync(reportId); var html = GenerateReportHtml(reportData); // Create PDF with headers/footers var pdf = await _renderer.RenderHtmlAsPdfAsync(html); // Add headers pdf.AddTextHeaders(new TextHeaderFooter { CenterText = reportData.Title, LeftText = "{date}", RightText = "{page} of {total-pages}", DrawDividerLine = true }); // Convert to bytes byte[] pdfBytes = pdf.BinaryData; // Cache for 5 minutes var cacheOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)); _cache.Set($"pdf_{reportId}", pdfBytes, cacheOptions); // Return file with proper headers Response.Headers.Add("Content-Disposition", $"inline; filename=report_{reportId}.pdf"); Response.Headers.Add("X-Content-Type-Options", "nosniff"); return File(pdfBytes, "application/pdf"); } catch (Exception ex) { // Log error return StatusCode(500, "Error generating PDF"); } } [HttpPost("merge")] public async Task<IActionResult> MergePdfs([FromBody] MergeRequest request) { if (request.PdfBytes == null || request.PdfBytes.Count < 2) { return BadRequest("At least 2 PDFs required for merging"); } // Convert byte arrays to PdfDocuments var pdfs = request.PdfBytes .Select(bytes => new PdfDocument(bytes)) .ToList(); // Merge PDFs var mergedPdf = PdfDocument.Merge(pdfs); // Apply consistent formatting mergedPdf.AddTextFooters(new TextHeaderFooter { CenterText = "Merged Document - Page {page}", FontSize = 10 }); // Clean up pdfs.ForEach(pdf => pdf.Dispose()); byte[] resultBytes = mergedPdf.BinaryData; mergedPdf.Dispose(); return File(resultBytes, "application/pdf", "merged.pdf"); } private string GenerateReportHtml(ReportData data) { return $@" <!DOCTYPE html> <html> <head> <style> @media print {{ .page-break {{ page-break-after: always; }} }} body {{ font-family: Arial, sans-serif; line-height: 1.6; }} table {{ width: 100%; border-collapse: collapse; }} th, td {{ border: 1px solid #ddd; padding: 8px; }} </style> </head> <body> <h1>{data.Title}</h1> <p>Generated: {DateTime.Now:yyyy-MM-dd HH:mm}</p> {data.HtmlContent} </body> </html>"; } } public class MergeRequest { public List<byte[]> PdfBytes { get; set; } } using IronPdf; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; [ApiController] [Route("api/[controller]")] public class PdfController : ControllerBase { private readonly IMemoryCache _cache; private readonly ChromePdfRenderer _renderer; public PdfController(IMemoryCache cache) { _cache = cache; _renderer = new ChromePdfRenderer(); // Configure for web optimization _renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait; _renderer.RenderingOptions.MarginTop = 40; _renderer.RenderingOptions.MarginBottom = 40; _renderer.RenderingOptions.EnableJavaScript = true; _renderer.RenderingOptions.WaitFor.RenderDelay(200); } [HttpGet("report/{reportId}")] public async Task<IActionResult> GenerateReport(string reportId) { // Check cache first if (_cache.TryGetValue($"pdf_{reportId}", out byte[] cachedPdf)) { return File(cachedPdf, "application/pdf", $"report_{reportId}.pdf"); } try { // Generate report data var reportData = await GetReportDataAsync(reportId); var html = GenerateReportHtml(reportData); // Create PDF with headers/footers var pdf = await _renderer.RenderHtmlAsPdfAsync(html); // Add headers pdf.AddTextHeaders(new TextHeaderFooter { CenterText = reportData.Title, LeftText = "{date}", RightText = "{page} of {total-pages}", DrawDividerLine = true }); // Convert to bytes byte[] pdfBytes = pdf.BinaryData; // Cache for 5 minutes var cacheOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)); _cache.Set($"pdf_{reportId}", pdfBytes, cacheOptions); // Return file with proper headers Response.Headers.Add("Content-Disposition", $"inline; filename=report_{reportId}.pdf"); Response.Headers.Add("X-Content-Type-Options", "nosniff"); return File(pdfBytes, "application/pdf"); } catch (Exception ex) { // Log error return StatusCode(500, "Error generating PDF"); } } [HttpPost("merge")] public async Task<IActionResult> MergePdfs([FromBody] MergeRequest request) { if (request.PdfBytes == null || request.PdfBytes.Count < 2) { return BadRequest("At least 2 PDFs required for merging"); } // Convert byte arrays to PdfDocuments var pdfs = request.PdfBytes .Select(bytes => new PdfDocument(bytes)) .ToList(); // Merge PDFs var mergedPdf = PdfDocument.Merge(pdfs); // Apply consistent formatting mergedPdf.AddTextFooters(new TextHeaderFooter { CenterText = "Merged Document - Page {page}", FontSize = 10 }); // Clean up pdfs.ForEach(pdf => pdf.Dispose()); byte[] resultBytes = mergedPdf.BinaryData; mergedPdf.Dispose(); return File(resultBytes, "application/pdf", "merged.pdf"); } private string GenerateReportHtml(ReportData data) { return $@" <!DOCTYPE html> <html> <head> <style> @media print {{ .page-break {{ page-break-after: always; }} }} body {{ font-family: Arial, sans-serif; line-height: 1.6; }} table {{ width: 100%; border-collapse: collapse; }} th, td {{ border: 1px solid #ddd; padding: 8px; }} </style> </head> <body> <h1>{data.Title}</h1> <p>Generated: {DateTime.Now:yyyy-MM-dd HH:mm}</p> {data.HtmlContent} </body> </html>"; } } public class MergeRequest { public List<byte[]> PdfBytes { get; set; } } Imports IronPdf Imports Microsoft.AspNetCore.Mvc Imports System.Threading.Tasks Imports Microsoft.Extensions.Caching.Memory <ApiController> <Route("api/[controller]")> Public Class PdfController Inherits ControllerBase Private ReadOnly _cache As IMemoryCache Private ReadOnly _renderer As ChromePdfRenderer Public Sub New(cache As IMemoryCache) _cache = cache _renderer = New ChromePdfRenderer() ' Configure for web optimization _renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Portrait _renderer.RenderingOptions.MarginTop = 40 _renderer.RenderingOptions.MarginBottom = 40 _renderer.RenderingOptions.EnableJavaScript = True _renderer.RenderingOptions.WaitFor.RenderDelay(200) End Sub <HttpGet("report/{reportId}")> Public Async Function GenerateReport(reportId As String) As Task(Of IActionResult) ' Check cache first Dim cachedPdf As Byte() = Nothing If _cache.TryGetValue($"pdf_{reportId}", cachedPdf) Then Return File(cachedPdf, "application/pdf", $"report_{reportId}.pdf") End If Try ' Generate report data Dim reportData = Await GetReportDataAsync(reportId) Dim html = GenerateReportHtml(reportData) ' Create PDF with headers/footers Dim pdf = Await _renderer.RenderHtmlAsPdfAsync(html) ' Add headers pdf.AddTextHeaders(New TextHeaderFooter With { .CenterText = reportData.Title, .LeftText = "{date}", .RightText = "{page} of {total-pages}", .DrawDividerLine = True }) ' Convert to bytes Dim pdfBytes As Byte() = pdf.BinaryData ' Cache for 5 minutes Dim cacheOptions = New MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(5)) _cache.Set($"pdf_{reportId}", pdfBytes, cacheOptions) ' Return file with proper headers Response.Headers.Add("Content-Disposition", $"inline; filename=report_{reportId}.pdf") Response.Headers.Add("X-Content-Type-Options", "nosniff") Return File(pdfBytes, "application/pdf") Catch ex As Exception ' Log error Return StatusCode(500, "Error generating PDF") End Try End Function <HttpPost("merge")> Public Async Function MergePdfs(<FromBody> request As MergeRequest) As Task(Of IActionResult) If request.PdfBytes Is Nothing OrElse request.PdfBytes.Count < 2 Then Return BadRequest("At least 2 PDFs required for merging") End If ' Convert byte arrays to PdfDocuments Dim pdfs = request.PdfBytes.Select(Function(bytes) New PdfDocument(bytes)).ToList() ' Merge PDFs Dim mergedPdf = PdfDocument.Merge(pdfs) ' Apply consistent formatting mergedPdf.AddTextFooters(New TextHeaderFooter With { .CenterText = "Merged Document - Page {page}", .FontSize = 10 }) ' Clean up pdfs.ForEach(Sub(pdf) pdf.Dispose()) Dim resultBytes As Byte() = mergedPdf.BinaryData mergedPdf.Dispose() Return File(resultBytes, "application/pdf", "merged.pdf") End Function Private Function GenerateReportHtml(data As ReportData) As String Return $" <!DOCTYPE html> <html> <head> <style> @media print {{ .page-break {{ page-break-after: always; }} }} body {{ font-family: Arial, sans-serif; line-height: 1.6; }} table {{ width: 100%; border-collapse: collapse; }} th, td {{ border: 1px solid #ddd; padding: 8px; }} </style> </head> <body> <h1>{data.Title}</h1> <p>Generated: {DateTime.Now:yyyy-MM-dd HH:mm}</p> {data.HtmlContent} </body> </html>" End Function End Class Public Class MergeRequest Public Property PdfBytes As List(Of Byte()) End Class $vbLabelText $csharpLabel 为了高效存储和检索,请考虑以下做法:完成时处理PdfDocument对象,用于大文件的流以避免内存问题,并为文件操作实现适当的错误处理。 字节数组格式使其易于集成到各种存储解决方案中,从本地文件系统到云平台。 了解更多关于在 ASP.NET 中提供 PDF 服务的信息。 对于Blazor 应用程序,字节数组可以实现流畅的 PDF 生成。 在实现MAUI PDF 查看时,字节数组提供了跨平台兼容性。 Razor 到 PDF 的转换功能可以高效地处理字节数组输出。 企业部署通常使用Azure Functions进行无服务器 PDF 生成。 根据Stack Overflow上关于PDF字节数组转换的讨论,处理大PDF文件时正确的内存管理至关重要。 Microsoft关于MemoryStream的文档为高效流处理提供了额外的见解。 对于生产环境部署,请考虑对 PDF 生成端点实施健康监控。 如何处理并发请求? 为了确保线程安全,每个请求都应创建新的ChromePdfRenderer实例。 对资源密集型 PDF 生成任务实施请求排队机制。 使用 async/await 模式进行 I/O 操作。 考虑使用分布式缓存来缓存经常请求的文档生成的 PDF 文件。 IronPDF性能指南提供了优化策略。 对于Docker 部署,容器资源限制会影响并发性。 使用Linux环境时,要密切监控系统资源。 企业系统通常会对 PDF 生成端点实施速率限制。 需要考虑哪些安全因素? 在生成 PDF 之前验证输入数据,以防止注入攻击。 实施文件大小限制以防止拒绝服务攻击。保存用户生成的内容时,对文件名进行清理。 传输敏感PDF数据时请使用HTTPS。 考虑对机密文件进行PDF加密。 PDF权限和密码功能可实现访问控制。 对于数字签名实现,字节数组可以保持证书的完整性。 考虑对不可信的PDF内容进行清理。 金融机构通常会采用基于 HSM 的签名机制来提高安全性。 如何监控绩效? 使用应用程序指标跟踪 PDF 生成时间。 监控高峰负载期间的内存使用情况。 对转换失败的情况进行日志记录。 使用 Application Insights 或类似的 APM 工具。 设置异常大的字节数组分配警报,这可能表明存在问题。 自定义日志集成可实现详细监控。 为了提升渲染性能,请跟踪 Chrome 引擎指标。 在实施批量操作时,监控资源利用模式。 企业系统通常会与集中式监控平台集成,以实现完全可观测性。 关于PDF字节数组转换,您应该记住什么? IronPDF 简化了 C# 中 PDF 到字节数组的转换,提供了有效而简单的方法来处理作为二进制数据的 PDF 文档。 无论你是构建API,管理文档数据库,还是创建Web应用程序,IronPDF的BinaryData和Stream属性提供了现代PDF处理所需的灵活性。 该库的完整功能集包括HTML 转 PDF 、 PDF 编辑功能和文档组织。 对于企业应用而言, PDF/A 合规性和数字签名等功能可确保符合监管要求。 详尽的文档涵盖了高级场景,包括表单创建、注释管理和辅助功能。 准备好探索IronPDF的功能了吗? 立即开始免费试用,体验可随您的应用需求扩展的许可模式。 在您的开发环境中测试库功能,并为您的特定需求确定最佳方法。 要了解 IronPDF 的更多有效功能,请查看其详尽的文档。 常见问题解答 如何使用IronPDF将C#表单转换为PDF? 您可以通过利用其直观的API将C#表单转换为PDF,该API允许您高效处理PDF转换,无需复杂代码。 为什么在.NET应用中将PDF文档转换为字节数组很重要? 将PDF文档转换为字节数组很重要,因为这可以启用数据库中的PDF存储,通过API传输,以及内存中处理文档内容,这些都是现代.NET应用程序中的关键操作。 使用IronPDF进行字节数组转换有哪些好处? IronPDF通过提供简易使用的API简化了字节数组转换,简化了流程,减少了复杂代码的需求,提高了开发效率。 IronPDF能否在内存中处理PDF转换? 是的,IronPDF可以在内存中处理PDF转换,让开发者无需将文件保存到磁盘即可管理文档内容。 使用IronPDF将PDF存储在数据库中是否可能? 是的,您可以通过使用IronPDF将PDF转换为字节数组,从而无缝集成数据库系统。 IronPDF如何帮助通过API传输PDF文件? IronPDF通过将PDF转换为字节数组,简化了通过API传输PDF文件,使其更易于通过网络协议发送和接收文档数据。 是什么使IronPDF的API对开发者来说直观? IronPDF的API专为开发者设计为直观,提供清晰明了的方法,简化复杂的PDF操作,提升生产力并缩短学习曲线。 Curtis Chau 立即与工程团队聊天 技术作家 Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。 相关文章 已更新2026年1月22日 如何使用 IronPDF 在 .NET 中创建 PDF 文档:完整指南 发现为开发人员创建PDF文件的有效方法。提升您的编码技能并简化您的项目。立即阅读文章! 阅读更多 已更新2026年1月21日 如何在 VB.NET 中合并 PDF 文件:完整教程 使用IronPDF合并PDF VB NET。学习使用简单的VB.NET代码将多个PDF文件合并为一个文档。包括逐步示例。 阅读更多 已更新2026年1月21日 C# PDFWriter 教程:在 .NET 中创建 PDF 文档 使用这份逐步指南了解如何高效地使用C# PDFWriter创建PDF。阅读文章提升您的技能! 阅读更多 DotNet Core 生成 PDF 文件如何在 VB.NET 中合并 PDF 文...
已更新2026年1月21日 如何在 VB.NET 中合并 PDF 文件:完整教程 使用IronPDF合并PDF VB NET。学习使用简单的VB.NET代码将多个PDF文件合并为一个文档。包括逐步示例。 阅读更多