如何解决从存储在 Microsoft SQL 数据库中的文件创建文件系统文件
标题听起来很混乱,但我不知道如何更好地描述它。
我的任务是将数据库从程序 A 迁移到程序 B。
程序 A 使用 MSSQL 数据库并将其所有文件存储在数据库中。
程序 B 以“正常”方式处理文件,即在文件系统中。
现在我必须使用 PHP 将数据库存储的文件提取、转换和下载为文件系统文件,但我尝试转换它们没有成功。
出于测试目的,我创建了一个简单的 .txt
文件,内容为 Test document for migration
,程序 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,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)
,它为我提供了一个很好的包含数据的关联数组。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_NUMERIC
、SQLSRV_FETCH_ASSOC
和 SQLSRV_FETCH_BOTH
然后结果集已经获取了,我不能用 sqlsrv_get_field
来设置每个的类型字段。
我不可能是第一个需要这样的东西的人,但我找不到任何关于它的东西。可能是我搜索错误,或者我误解了一个概念。
解决方法
我找到了解决方案!
我测试了多个在线工具来解码字符串并找出编码是什么。
甚至尝试过 hex2bin()
但所有工具都告诉我它不是有效的十六进制字符串。
直到我偶然发现了 this 天赐工具,该工具将无效的十六进制转换为 ?
,但将其余部分转换为:
?Test document for migration
从那时起,很容易推断出0x
是麻烦制造者。删除它后,转换就像一个魅力,我可以“转换”更复杂的文件,如 .doc
。代码如下:
file_put_contents(
// 'DESCRIPTION' is the file name
'files/' . $dbDocument['DESCRIPTION'] .
// 'FILETYPE' is the extension
'.' . mb_strtolower($dbDocument['FILETYPE']),// 'DOCUMENT' is the document content hex encoded with prepended '0x'
hex2bin(str_replace('0x','',$dbDocument['DOCUMENT']))
);
,
这不是一种格式,而是某些数据库管理工具(例如 SSMS)显示二进制数据的方式。数据已经是二进制的,不需要转换。
读取大对象 (LOB) 就好像它们是数字或字符串一样确实很慢,将整个文档缓存在服务器和客户端内存中,即使该对象不会被重用,并且甚至不需要保存在内存中。毕竟,SQL Server 中的 BLOB
可以是 2GB 甚至更多。这就是几乎所有数据库和数据访问库都允许将 LOB 作为文件流处理的原因。
Microsoft 的 PHP 文档示例展示了如何将 LOB 读取为 PDO 和 SQLSRV 的文件流。
复制示例,此参数化查询将搜索用户的图片:
/* 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,$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,$getAsType);
使用指定类型检索第一个字段,在本例中为流。这不会加载实际内容。
fpassthru 将流内容直接复制到输出。图片可能是 2GB,但它永远不会保存在 Web 服务器的内存中。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。