You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用sp_invoke_external_rest_endpoint转存SQL Server中PDF至Blob Storage遇损坏问题

问题

我正尝试将Azure SQL Server数据库表中的PDF文件迁移至Blob Storage,但遇到了问题。计划使用SQL Server存储过程sp_invoke_external_rest_endpoint完成该任务,然而发现文件在传输前被转换为Unicode,导致损坏。

我的代码

DECLARE @bytes VARBINARY(MAX) = NULL;
DECLARE @fileName VARCHAR(MAX) = NULL;

SELECT TOP 1 @bytes = Bytes, @fileName = FileName
FROM #TempPdf
OPTION(RECOMPILE);

DECLARE @response VARCHAR(MAX) = NULL;
DECLARE @storageContainerUrl VARCHAR(MAX) = 'https://mystorage.blob.core.windows.net/testing-pdfs/';

DECLARE @url VARCHAR(MAX) = CONCAT(@storageContainerUrl, @fileName);
DECLARE @payload VARCHAR(MAX) = @bytes;
DECLARE @payloadLength INT = DATALENGTH(@payload);

DECLARE @headers VARCHAR(1000) = JSON_OBJECT
(
        'content-length': @payloadLength,
        'x-ms-content-length': CAST(@payloadLength AS VARCHAR(20)),
        'content-type': 'application/x-www-form-urlencoded',
        'Accept': 'application/xml',
        'x-ms-blob-type': 'BlockBlob'
);

SELECT
@bytes AS Bytes,
DATALENGTH(@bytes) AS BytesLength,
@payload AS Payload,
DATALENGTH(@payload) AS PayloadLength;

EXEC sp_invoke_external_rest_endpoint
    @url = @url,
    @method = 'PUT',
    @headers = @headers,
    @payload = @payload,
    @credential = 'MyCredentials',
    @response = @response OUTPUT;

SELECT CAST(@response AS XML) AS Response;

字节数据(VARBINARY(MAX))和负载(VARCHAR(MAX))的数据长度均为88,698,符合该PDF的正确大小,但Blob Storage中文件大小为127KiB,远大于应有的87KiB。推测是sp_invoke_external_rest_endpoint的@payload参数为NVARCHAR(MAX),将8位变量转换为Unicode导致文件损坏。

请问是否有办法使用sp_invoke_external_rest_endpoint避免文件损坏,或在存储过程中实现该任务的替代方案?如果实在不行,只能用C#创建临时任务来完成。


解决方案

1. sp_invoke_external_rest_endpoint的限制说明

sp_invoke_external_rest_endpoint的@payload参数仅支持NVARCHAR(MAX)类型,传入VARCHAR(MAX)或VARBINARY(MAX)时会自动转换为Unicode编码,导致二进制数据膨胀、文件损坏。目前没有直接绕过该转换的方法,因此该存储过程不适合传输二进制文件。

2. SQL端替代方案

方案一:使用OPENROWSET直接写入Blob

如果SQL Server版本支持,可通过OPENROWSET直接将二进制数据写入Blob Storage,无需编码转换:

INSERT INTO OPENROWSET(
    BULK N'https://mystorage.blob.core.windows.net/testing-pdfs/your-file-name.pdf',
    FORMATFILE='nul',
    SINGLE_BLOB
)
SELECT Bytes FROM #TempPdf WHERE FileName = 'your-file-name.pdf';

需提前配置SQL Server的外部数据源和对应凭据,确保具备Blob写入权限。

方案二:利用Azure SQL内置Blob集成

若使用Azure SQL数据库,可通过关联外部数据源直接导出二进制数据:

-- 创建外部数据源
CREATE EXTERNAL DATA SOURCE BlobStorage
WITH (
    TYPE = BLOB_STORAGE,
    LOCATION = 'https://mystorage.blob.core.windows.net/testing-pdfs',
    CREDENTIAL = MyCredentials
);

-- 导出单文件
SELECT Bytes
FROM #TempPdf
WHERE FileName = 'your-file-name.pdf'
INTO EXTERNAL FILE BlobStorage
WITH (FILE_NAME = 'your-file-name.pdf');

该方式由Azure SQL直接处理二进制传输,完全避免编码问题。

方案三:部署CLR存储过程

编写.NET CLR存储过程,直接读取VARBINARY(MAX)数据并调用Azure Blob SDK上传。需在SQL Server中启用CLR集成,并部署签名的程序集,可精准控制二进制流传输。

3. 外部脚本方案(C#临时任务)

若SQL端方案无法实施,C#临时任务是可靠选择:

  • 编写控制台应用,连接Azure SQL读取VARBINARY(MAX)类型的PDF数据
  • 使用Azure.Storage.Blobs SDK直接上传二进制流到Blob Storage
  • 可通过Azure Functions或本地任务调度实现批量迁移

这种方式彻底规避SQL端的编码转换问题,适合大规模或复杂的文件迁移场景。


内容的提问来源于stack exchange,提问作者null_pointer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:50:04