IRONSOFTWAREHOME
使用IRONPDF

如何使用IronPDF在C#中合并两个PDF字节数组

Curtis Chau
Curtis Chau
Updated: 2026年3月9日

在ASP.NET中使用C#从数据库检索PDF文件需要三个步骤:查询数据库表中的二进制BLOB列,使用IronPDF将字节加载到File()响应将字节返回到浏览器。 IronPDF 负责渲染、水印和安全功能,让您可以专注于数据访问逻辑。

如何为 ASP.NET 安装 IronPDF?

在编写任何 PDF 获取代码之前,请通过 NuGet 包管理器将 IronPDF 添加到您的项目中:

PM > Install-Package IronPdf

安装后,在调用任何IronPDF方法之前,在appsettings.json中设置您的许可证密钥:

IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";

IronPDF 支持.NET 10 、.NET 8、.NET 6 和 .NET Framework 4.6.2+。 它可在 Windows、Linux 和 macOS 上运行,无需任何额外的依赖项或无头浏览器安装。 提供免费试用许可证供您评估。

如何设置 SQL Server 数据库表?

最常见的方法是将PDF文件存储为SQL Server VARBINARY(MAX)列中的二进制数据。 这样可以将文档及其元数据保存在单个表中,简化备份,并避免文件系统路径管理。

使用以下 SQL 脚本创建存储表:

// SQL Server table definition (run this in SSMS or via EF migrations)
// CREATE TABLE PdfDocuments (
//     ID INT 身份(1,1) PRIMARY KEY,
//     文件名 NVARCHAR(255) NOT NULL,
//     文件内容 VARBINARY(MAX) NOT NULL,
//     上传于 日期时间2 DEFAULT GETUTCDATE()
// );

表格存在后,在appsettings.json中配置连接字符串:

// appsettings.json snippet (not C# -- shown as reference)
// "ConnectionStrings": {
//   "DefaultConnection": "Server=localhost;Database=PdfStorage;Integrated Security=True;"
// }

通过在Program.cs中使用依赖注入注册连接字符串:

using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);

IronPdf.License.LicenseKey = builder.Configuration["IronPdf:LicenseKey"];

var app = builder.Build();
app.MapControllers();
app.Run();

如何在 ASP.NET Core 中从 SQL Server 检索 PDF 文件?

检索模式遵循三个步骤:打开连接,执行参数化的SELECT查询,并将二进制列读入byte[]。 然后IronPDF将该数组加载到 PdfDocument 对象中以进行可选处理,然后流式传输到客户端。

构建 API 控制器

创建一个控制器,该控制器公开用于内联显示和文件下载的 GET 端点:

using IronPdf;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;

[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
    private readonly string _connectionString;

    public PdfController(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? throw new InvalidOperationException("Connection string not found.");
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetPdf(int id)
    {
        byte[] pdfBytes = await RetrievePdfBytesAsync(id);
        if (pdfBytes is null || pdfBytes.Length == 0)
            return NotFound("PDF document not found.");

        // Load into IronPDF for validation or optional modification
        using var pdfDocument = new PdfDocument(pdfBytes);

        // Inline display -- browser opens PDF viewer
        Response.Headers.Append("Content-Disposition", "inline; filename=\"document.pdf\"");
        return File(pdfDocument.BinaryData, "application/pdf");
    }

    private async Task<byte[]> RetrievePdfBytesAsync(int documentID)
    {
        await using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync();

        const string query = "SELECT 文件内容 FROM PdfDocuments WHERE ID = @ID";
        await using var command = new SqlCommand(query, connection);
        command.Parameters.AddWithValue("@ID", documentID);

        var result = await command.ExecuteScalarAsync();
        return result as byte[] ?? Array.Empty<byte>();
    }
}

该控制器使用参数化查询来防止SQL注入,并使用SqlCommandPdfDocument类验证字节数组并公开BinaryData属性以进行流式传输。

返回一个可供下载的指定文件

当用户需要保存文档而不是内联查看时,将attachment并传递原始文件名:

[HttpGet("download/{id}")]
public async Task<IActionResult> DownloadPdf(int id)
{
    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string query = "SELECT 文件名, 文件内容 FROM PdfDocuments WHERE ID = @ID";
    await using var command = new SqlCommand(query, connection);
    command.Parameters.AddWithValue("@ID", documentID);

    await using var reader = await command.ExecuteReaderAsync();
    if (!await reader.ReadAsync())
        return NotFound("Document not found.");

    var fileName = reader.GetString(reader.GetOrdinal("文件名"));
    var pdfBytes = (byte[])reader["文件内容"];

    using var pdfDocument = new PdfDocument(pdfBytes);
    return File(pdfDocument.BinaryData, "application/pdf", fileName);
}

attachment。 ASP.NET Core 可以正确处理包含空格的文件名。

如何给下载的PDF文件添加水印?

检索后最实用的操作之一是在提供文档之前,在每一页上添加水印。 这对于机密报告、文件草稿或任何需要明显安全标记的文件都很有用。

使用 IronPDF 应用 HTML 水印

IronPDF 的水印 API 接受任何 HTML 字符串,这意味着您可以使用内联 CSS 设置水印文本的样式。 将透明度调低,使底层内容仍然清晰可读:

[HttpGet("watermarked/{id}")]
public async Task<IActionResult> GetWatermarkedPdf(int id)
{
    byte[] pdfBytes = await RetrievePdfBytesAsync(id);
    if (pdfBytes is null || pdfBytes.Length == 0)
        return NotFound("PDF document not found.");

    using var pdfDocument = new PdfDocument(pdfBytes);

    // HTML watermark applied to every page
    string watermarkHtml = "<h2 style='color:red; opacity:0.4; font-family:Arial;'>CONFIDENTIAL</h2>";
    pdfDocument.ApplyWatermark(
        watermarkHtml,
        rotation: 30,
        verticalAlignment: VerticalAlignment.Middle,
        horizontalAlignment: HorizontalAlignment.Center
    );

    return File(pdfDocument.BinaryData, "application/pdf");
}

ApplyWatermark方法接受标准的HTML和CSS,因此您可以完全控制字体、颜色、不透明度和位置。 水印会自动应用于文档中的所有页面。 有关PDF 的其他操作功能,包括添加图像印章、添加页眉和页脚或合并多个文档,请参阅 IronPDF 文档。

如何将上传的PDF文件存储回SQL Server?

完成往返流程需要一个上传端点,该端点读取传入的表单文件并将其写入数据库。 这与上述检索端点配合使用,构成了一个完整的文档管理系统:

[HttpPost("upload")]
public async Task<IActionResult> UploadPdf(IFormFile file)
{
    if (file is null || file.Length == 0)
        return BadRequest("No file uploaded.");

    if (!file.内容类型.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
        return BadRequest("Only PDF files are accepted.");

    using var memoryStream = new MemoryStream();
    await file.CopyToAsync(memoryStream);
    byte[] pdfBytes = memoryStream.ToArray();

    // Validate using IronPDF before storage
    using var pdfDocument = new PdfDocument(pdfBytes);

    await using var connection = new SqlConnection(_connectionString);
    await connection.OpenAsync();

    const string insertQuery = @"
        INSERT INTO PdfDocuments (文件名, 文件内容)
        VALUES (@文件名, @文件内容);
        SELECT SCOPE_IDENTITY();";

    await using var command = new SqlCommand(insertQuery, connection);
    command.Parameters.AddWithValue("@文件名", file.文件名);
    command.Parameters.AddWithValue("@文件内容", pdfDocument.BinaryData);

    var newID = Convert.ToInt32(await command.ExecuteScalarAsync());
    return Ok(new { id = newID, fileName = file.文件名 });
}

使用PdfDocument进行存储前验证,以确保只有可解析且结构良好的PDF文件进入数据库。 如果字节数组损坏或被截断,IronPDF会抛出一个异常,您可以捕获并作为400 Bad Request响应返回。

PDF 存储的关键表和列类型有哪些?

你使用的模式会影响查询性能和存储效率。 下表显示了 SQL Server 的推荐列配置:

推荐的 PDF BLOB 存储 SQL Server 架构
数据类型翻译目的
IDINT 身份主键,自动递增
文件名NVARCHAR(255)下载头文件的原始文件名
文件内容VARBINARY(MAX)原始PDF二进制数据(BLOB)
内容类型NVARCHAR(100)MIME 类型,例如 application/pdf
文件大小(字节)大情报用于配额管理的存储大小
上传于日期时间2用于审计的UTC时间戳
上传者NVARCHAR(100)用于访问控制的用户身份

对于需要 SQL Server 文件流的大型系统,Microsoft 将FILESTREAM 功能作为替代方案记录下来,该功能将大型 BLOB 存储在文件系统中,同时仍可通过 T-SQL 进行查询。 然而,对于服务几个百兆字节的文档的大多数ASP.NET应用程序,VARBINARY(MAX)行内存储效果良好,并简化了部署。

如何处理错误并优化性能?

生产环境中可靠的 PDF 检索需要在每个层面进行错误处理——数据库、IronPDF 和 HTTP 响应。 下表总结了关键做法:

PDF检索的错误处理和性能模式
忧虑建议原因
连接处置await using语句防止连接池耗尽
PDF文档处理using语句及时释放未管理的内存
SQL注入仅限参数化查询防止恶意输入篡改查询
文件类型验证检查 MIME 类型和魔数字节存储前阻止非 PDF 文件的上传
大文件处理使用FileStreamResult流响应避免将整个文件加载到服务器内存中
缓存使用IMemoryCacheIDistributedCache减少重复的数据库往返次数
异步操作async / await过程在磁盘和网络等待期间保持线程空闲。

对于连接字符串管理,将值存储在appsettings.json中,切勿在源文件中硬编码。 在本地开发期间使用 ASP.NET Core内置的密钥管理,在生产环境中使用 Azure Key Vault 或 AWS Secrets Manager。 永远不要将连接字符串或许可证密钥提交到源代码控制系统中。

在服务大型PDF文件时,考虑返回由FileStreamResult,而不是将整个字节数组加载到内存中。 对于超过 100 MB 的大型文档, SQL Server FILESTREAM API可以直接从文件系统进行分块流式传输。

缓存常用文档

如果某些PDF文件被反复请求,例如条款和条件文档或产品目录,则在IMemoryCache中缓存字节数组可以避免反复的数据库来回。 在IMemoryCache,然后将其注入到控制器中,并在查询数据库之前检查缓存。 设置一个与文档预期更新频率相匹配的绝对过期时间。 当文档更新时,按键删除缓存条目,以便下次请求获取新版本。

对于分布式场景,例如具有多个服务器实例的负载平衡部署,将IMemoryCache替换为由Redis或SQL Server支持的IDistributedCache。 ASP.NET Core 的分布式缓存抽象使控制器代码几乎完全相同; 仅Program.cs中的注册发生变化。

如何使用 IronPDF 实现跨平台 PDF 检索?

IronPDF 可在 Linux、Windows 和 macOS 上运行,无需单独安装 Chromium 或进行任何配置更改。 同一个 NuGet 包适用于所有平台,因此无论您部署到哪个平台,您的 PDF 控制器都能正常工作:

  • 带有 IIS 的 Windows Server
  • 在 Docker 或 Kubernetes 上运行的 Ubuntu 容器
  • Azure 应用服务(Linux 或 Windows)
  • AWS Elastic Beanstalk

部署到 Docker 和 Linux

对于 Docker 部署,请将 IronPDF 依赖项添加到 Dockerfile 中。IronPDF Linux 文档提供了 Debian 和 Alpine 基础镜像所需的确切 apt 软件包。 典型的多阶段 Dockerfile 会在运行时镜像阶段安装操作系统依赖项,然后将已发布的 ASP.NET 应用程序复制到其上。使用 Azure 时, Azure 部署指南涵盖了应用服务配置,包括支持大规模 PDF 渲染的内存和 CPU 设置。

由于 IronPDF 捆绑了自己的基于 Chromium 的渲染引擎,因此您无需在服务器上安装单独的浏览器。 与需要系统级浏览器的解决方案相比,这大大简化了 Linux 容器的设置。 IronPDF 团队每次发布新版本都会针对最常见的 Linux 基础镜像进行测试,因此您可以放心,Alpine 或 Debian 容器可以开箱即用。

使用 Entity Framework Core 而不是 ADO.NET

IronPDF 还集成了Entity Framework Core ,作为原始 ADO.NET 的替代方案。 如果您的项目已经使用EF Core,您可以将byte[]属性,并让EF处理查询生成。 这种方法可以显著减少样板代码,并且可以通过 EF 的 LINQ 提供程序更轻松地添加筛选、分页和审计行为。

这样做的代价是,EF Core 会将整个 BLOB 作为实体图的一部分加载到内存中。 对于非常大的PDF文件,考虑使用原生ADO.NET或EF Core的FromSql方法和仅选择字节数组列而不是完整实体的投影。

下一步计划是什么?

使用C#和IronPDF在ASP.NET Core中从SQL Server数据库检索PDF文件遵循一个明确的模式:使用参数化SELECT查询BLOB列,将字节加载到Content-Disposition头返回二进制数据。 IronPDF 增加了在文档离开服务器之前对其进行验证、添加水印、合并或保护的功能。

要进一步了解 IronPDF 的文档管理功能,请探索以下资源:

IronPDF 从 HTML 生成 PDF——动态生成 PDF 并将其直接存储到您的数据库中 IronPDF 合并和拆分 PDF——将检索到的文档合并成单个响应

首先获取IronPDF 的免费试用许可证,即可不受限制地测试所有功能。 该试验产生带有水印的输出; 使用付费许可证密钥即可去除水印。 本文中使用的每种方法的完整API 参考文档和代码示例均可在 IronPDF 网站上找到。

如果您的项目已在使用 Entity Framework Core, IronPDF EF Core 集成指南将展示如何在保持 IronPDF 处理管道不变的情况下,用实体模型替换原始 ADO.NET。对于使用 .NET 10 和最新 ASP.NET Core 功能的团队,此处描述的模式无需修改即可使用——IronPDF 支持所有活跃的 .NET LTS 和 STS 版本。

请查看IronPDF 定价页面,找到适合您部署的许可证级别。 单个开发者许可证涵盖本地开发和测试; SaaS 产品和具有多个服务器的本地部署均可获得再分发许可。

Curtis Chau
技术作家

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

...
阅读更多

相关文章

Key in blue circle

立即获取免费的 30 天试用版密钥

无任何限制。100% 解锁。无需信用卡。

bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
预约您的免费现场演示
Booking Badge

深受全球数百万工程师信赖

Iron Software 的客户徽标
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户