从存储在Microsoft SQL数据库中的文件创建文件系统文件

2022-08-02 00:00:00 file hex filesystems php

标题听起来令人困惑,但我不知道如何更好地描述它。

我的任务是将数据库从程序A迁移到程序B。
程序A使用MSSQL数据库并将其所有文件存储在该数据库中。
程序B处理以正常方式存储的文件,即存储在文件系统中。

现在我必须使用PHP将数据库存储的文件提取、转换并下载到文件系统文件,但尝试转换它们失败。

出于测试目的,我创建了一个内容为Test document for migration的简单.txt文件,程序A将其存储在数据库中:

0x5465737420646F63756D656E7420666F72206D6967726174696F6E'

这是什么格式,以及如何将其转换为普通document.txt文件?

编辑

非常感谢@PanagiotisKanavos。现在可以使用流:

$query = "select top(1) DESCRIPTION, FILETYPE, DOCUMENT from dbo.Documents;";
$stmt = sqlsrv_query($this->sqlsrv_conn, $query);
if (sqlsrv_fetch($stmt)) {
    $document = sqlsrv_get_field($stmt, 2, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
    $fileName = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
    $ext = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
    file_put_contents(
        $fileName . '.' . $ext,
        stream_get_contents($document),
    );
} 
现在,对所有文件执行此操作的最有效方法是什么?我必须对每一行执行查询吗?

通过PDO,我可以使用$stmt->fetchAll(FETCH_ASSOC),它为我提供了一个包含数据的漂亮的ASSOC数组。
sqlsrv具有类似的sqlsrv_fetch_array功能,php.net和[docs.microsoft]通过以下示例进行说明:

while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
      echo $row['LastName'].", ".$row['FirstName']."<br />";
}

但就我所能进行的搜索而言,我找不到一种方法来循环遍历结果集,除非读取它,然后在流和字符串类型混合的情况下分别读取每一行。sqlsrv_fetch_array只接受SQLSRV_FETCH_NUMERICSQLSRV_FETCH_ASSOCSQLSRV_FETCH_BOTH,然后结果集已经获取,不能使用sqlsrv_get_field来设置每个字段的类型。
我不可能是第一个需要这样东西的人,但我找不到任何关于它的东西。可能我搜索错误,或者我误解了一个概念。


解决方案

这不是一种格式,它是某些数据库管理工具(如SSMS)显示二进制数据的方式。数据已经是二进制数据,不需要转换。

读取大对象(LOB)就像它们是数字或字符串一样非常慢,在服务器和客户端内存中缓存整个文档,即使对象不会被重用,甚至不需要保存在内存中。毕竟,SQL Server中的BLOB可以是2 GB甚至更大。这就是为什么几乎所有数据库和数据访问库都允许将LOB作为文件流处理。

Microsoft的PHP文档示例显示了如何将PDO和SQLSRV的LOB读取为文件流。

复制该示例,此参数化查询将搜索用户的图片:

/* Get the product picture for a given product ID. */  
$tsql = "SELECT LargePhoto   
         FROM Production.ProductPhoto AS p  
         JOIN Production.ProductProductPhoto AS q  
         ON p.ProductPhotoID = q.ProductPhotoID  
         WHERE ProductID = ?";  
  
$params = array(&$_REQUEST['productId']);  
  
/* Execute the query. */  
$stmt = sqlsrv_query($conn, $tsql, $params);  

不是将整个图片作为单个值读取,而是以文件流的形式加载:

$getAsType = SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY);  
if ( sqlsrv_fetch( $stmt ) )  
{  
   $image = sqlsrv_get_field( $stmt, 0, $getAsType);  
   fpassthru($image);  
}  
else  
{  
     echo "Error in retrieving data.</br>";  
     die(print_r( sqlsrv_errors(), true));  
}  

$getAsType = SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY);指定将以流的形式检索数据。

$image = sqlsrv_get_field( $stmt, 0, $getAsType);使用指定的类型检索第一个字段,在本例中为流。这不会加载实际内容。

fpassthru将流内容直接复制到输出。图片大小可能为2 GB,但永远不会保存在Web服务器的内存中。

相关文章