无法从 MSSQL 获取变量二进制数据


Cannot fetch varbinary data from MSSQL

Background

开发环境:

PHP 7.0.3 与 Apache 2.4.16 在 Windows 10 x64

SQL Server 2014 Standard

服务器在相应的文件列上启用了文件流。

尝试安装sqlsvr驱动程序,但由于缺乏对PHP7的支持而失败

使用驱动程序SQL Server从 ODBC 访问 SQL 服务器

用于将图像数据插入 MSSQL 的 PHP 代码

$link = @new 'PDO("odbc:Driver={SQL Server};Server=$server;Database=$db", $user, $password);
$stmt = $link->prepare("INSERT INTO [Attachment] (AttID, Seq , ModuleCde, AppID, StaffID , FileName , [File]) VALUES ( NEWID() , ? , ? , ? , ? , ? , ? )");
$stmt->bindValue(1,$_POST["Seq"],PDO::PARAM_INT);
$stmt->bindValue(2,$_POST["ModuleCde"],PDO::PARAM_STR);
$stmt->bindValue(3,$_POST["AppID"],PDO::PARAM_STR);
$stmt->bindValue(4,$_SESSION["StaffID"],PDO::PARAM_STR);
$stmt->bindValue(5,$_FILES["file"]["name"][$_POST["Seq"]],PDO::PARAM_STR);
$stmt->bindValue(6,file_get_contents($_FILES["file"]["tmp_name"][$_POST["Seq"]]),PDO::PARAM_STR);
$stmt->execute();

用于保存 MSSQL 图像数据的 PHP 代码

$link = @new 'PDO("odbc:Driver={SQL Server};Server=$server;Database=$db", $user, $password);
$stmt = $link->prepare("SELECT DATALENGTH([File]) AS [Size] , CONVERT(NVARCHAR(MAX),[File],2) AS [File] FROM [Attachment] WHERE [ModuleCde] = ? AND [AppID] = ? AND [Seq] = ? AND [StaffID] = ?");
$stmt->bindValue(1,$_GET["ModuleCde"],PDO::PARAM_STR);
$stmt->bindValue(2,$_GET["AppID"],PDO::PARAM_STR);
$stmt->bindValue(3,$_GET["Seq"],PDO::PARAM_STR);
$stmt->bindValue(4,$_SESSION["StaffID"],PDO::PARAM_STR);
$stmt->execute();
$stmt->bindColumn(2,$img,PDO::PARAM_LOB, 0);
$stmt->fetch(PDO::FETCH_ASSOC);
file_put_contents( "file" , $img );

文件已成功上传到 SQL 服务器。打开SQL Server的DATA目录,所有上传的文件都可以通过画图打开。

但是从上面的图像保存代码中提取的文件已损坏。文件与原始文件缺少一些字节。

尝试的方法

fwrite( fopen("file","w+") , strtolower("0x".str_replace("'0","",$img)) );

以及此链接中所述的方法。

带有 MSSQL 的 PHP 显示来自变量二进制字段的原始数据

但两者都没有运气,文件似乎也已损坏。

任何帮助,不胜感激。

编辑 2016-02-25

已按如下所示修改 SQL 语句,但输出文件仍已损坏。

 $link->prepare("SELECT DATALENGTH([File]) AS [Size] , [File] FROM [Attachment] WHERE [ModuleCde] = ? AND [AppID] = ? AND [Seq] = ? AND [StaffID] = ?");

我现在正在尝试更改不同的SQL服务器ODBC驱动程序,以查看文件是否可以成功输出。

编辑 2016-08-09

使用更新的 MSSQL PDO 驱动程序 PHP 7。一切都很迷人。

现在,我们不再需要使用此新驱动程序convert varbinary 数据。

参考链接

当插入到包含变量的表中时,数据类型的选择对PHP PDO非常重要。

应使用PDO::PARAM_LOB,而不是使用PDO::PARAM_STR作为文件列

要确保 DB 通过驱动程序输出正确的文件,请将 VARBINARY(MAX) 字段转换为十六进制字符串,并使用此方法将其正常移交给 PHP CONVERT(VARCHAR(MAX),[FileColumn],2)

将列转换为VARCHAR(MAX)而不是NVARCHAR(MAX),因为十六进制字符串不需要任何对 Unicode 的支持,并保留纯十六进制字符串。

然后,十六进制

字符串可以通过PHP函数有效地从十六进制数据转换回二进制hex2bin()

解决方案

$link = @new 'PDO("odbc:Driver={SQL Server};Server=$server;Database=$db", $user, $password);
$stmt = $link->prepare("SELECT DATALENGTH([File]) AS [Size] , CONVERT(VARCHAR(MAX),[File],2) AS [File] FROM [Attachment] WHERE [ModuleCde] = ? AND [AppID] = ? AND [Seq] = ? AND [StaffID] = ?");
$stmt->bindValue(1,$_GET["ModuleCde"],PDO::PARAM_STR);
$stmt->bindValue(2,$_GET["AppID"],PDO::PARAM_STR);
$stmt->bindValue(3,$_GET["Seq"],PDO::PARAM_STR);
$stmt->bindValue(4,$_SESSION["StaffID"],PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
file_put_contents( "file" , hex2bin($result["File"]) );

附加信息

当从SQL Server Native Client 11.0ODBC Driver 11 for SQL Server中选择varbinary列时,既不将列转换为varchar,PHP都会输出一个不需要的损坏的二进制结果。但是当我将列转换为 VARCHAR(MAX) 时,驱动程序SQL Server成功地将十六进制字符串返回给我。我怀疑此操作是错误或驱动程序问题。

如果要存储二进制数据,请不要在选择时将其转换为 NVARCHAR() - 尝试从中删除 CONVERT():

$stmt = $link->prepare("SELECT DATALENGTH([File]) AS [Size] , CONVERT(NVARCHAR(MAX),[File],2) AS [File] FROM [Attachment] WHERE [ModuleCde] = ? AND [AppID] = ? AND [Seq] = ? AND [StaffID] = ?");

是这样的:

$stmt = $link->prepare("SELECT DATALENGTH([File]) AS [Size], [File] FROM [Attachment] WHERE [ModuleCde] = ? AND [AppID] = ? AND [Seq] = ? AND [StaffID] = ?");

您还希望确保列VARBINARY(MAX)

插入数据时,我认为您需要使用SQLSRV_ENCODING_BINARY(而不是PDO::PARAM_STR