【问题标题】:Cannot fetch varbinary data from MSSQL无法从 MSSQL 获取 varbinary 数据
【发布时间】:2016-06-06 09:28:49
【问题描述】:

背景

开发环境:

Windows 10 x64 上的 PHP 7.0.3 和 Apache 2.4.16

SQL Server 2014 标准版

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

尝试安装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目录,所有上传的文件都可以用Paint打开了。

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

方法尝试

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

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

Php with MSSQL display raw data from varbinary field

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

感谢任何帮助。

编辑 2016-02-25

修改SQL语句如下,但输出文件仍然损坏。

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

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

编辑 2016-08-09

使用适用于 PHP 7 的更新的 MSSQL PDO 驱动程序。一切都以魅力再次运行。

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

Reference Link

【问题讨论】:

    标签: php sql-server pdo sql-server-2014 php-7


    【解决方案1】:

    当插入包含 varbinary 的表时,数据类型的选择对于 PHP PDO 非常重要。

    PDO::PARAM_LOB 应使用 PDO::PARAM_STR 代替文件列

    为确保DB通过驱动输出了正确的文件,将VARBINARY(MAX)字段转换为十六进制字符串,并使用CONVERT(VARCHAR(MAX),[FileColumn],2)这个方法正常交给PHP处理

    将列转换为 VARCHAR(MAX) 而不是 NVARCHAR(MAX),因为 HEX 字符串不需要任何对 Unicode 的支持,并留下一个普通的 HEX 字符串。

    然后可以通过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 成功地向我返回了一个十六进制字符串。我怀疑此操作是错误或驱动程序问题。

    【讨论】:

    • 我要哭了!!我一直在努力处理来自 mssql 服务器的 php pdo 和 varbinary 数据……您的回答很有帮助!我可以发送电子拥抱吗? :D 现在...我在哪里可以了解更多信息?这种“技术”叫什么名字?或类似的东西,所以我可以更多地谷歌并获得更多知识
    • 很高兴这对您有所帮助。这不是一种技术,我只是通过反复试验得到这个解决方案。因为我发现有一些关于 php 和 ODBC 驱动程序的错误报告指出这些驱动程序没有输出有效的 Unicode 数据。我担心varbinary 数据也会被处理为 Unicode,所以我尝试将其转换为不会被驱动程序错误解释的十六进制字符串。尝试之后,我成功地向 PHP 返回了一个有效的十六进制字符串。然后我将那串字符串转换为文件。这就是诀窍。
    • 当我们停留在一种数据类型时,尝试对其进行处理/编码以使其成为纯文本。这是我做的伎俩。
    • 在找到这个方法之前,我只是将带有base64_encode的文件转换并插入到数据库的text列中,然后使用base64_decode:D将其返回到文件中
    【解决方案2】:

    如果您要存储二进制数据,请不要在选择时将其转换为 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

    【讨论】:

    • 谢谢你的回复,因为我无法安装sqlsvr PDO驱动所以我怕我不能使用SQLSRV_ENCODING_BINARY
    • @MiKeNeko,PDO::PARAM_LOB 会更有用吗?它为 PDO 和数据库驱动程序提供了以不同方式处理数据的线索,而不仅仅是字符串。
    • @Ryan Vincent 感谢您的提示,我发现问题是 SQL 服务器驱动程序问题,现在问题已解决。
    猜你喜欢
    • 2014-02-28
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多