【问题标题】:How to recover a gzcompressed string stored in PostgreSQL ByteA using PHP PDO Object?如何使用 PHP PDO 对象恢复存储在 PostgreSQL ByteA 中的 gzcompressed 字符串?
【发布时间】:2016-10-13 11:38:55
【问题描述】:

我在理解 PHP 如何使用 postgreSQL 绑定处理 ByteA 时遇到问题。

出于记录和归档的目的,我将文件存储在ByteA 列文件中,这些文件与我的 PHP/Apache 服务器一起提供。对于存储,我使用gzencode() 压缩数据,然后在使用pg_escape_bytea() 存储之前转义字符串:

// Compress:
if($compress) {
    $data = gzencode($data, 9);
}
// PostgreSQL ByteA Escaping:
$data = pg_escape_bytea($data);

我还有一个页面允许用户检索以前提供的文件。但我无法成功压缩一个,我不知道为什么:

$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

/*
$dbCur = $webConn->prepare("SET bytea_output = 'escape';");
$dbCur->execute();
//print_r($dbCur->errorinfo());
*/

$dbCur = $webConn->prepare("SELECT * FROM logs.webservice WHERE Id=?;");
$dbCur->bindParam(1, $id);    
$dbCur->execute();
//print_r($dbCur->errorinfo());

$row = $dbCur->fetch(PDO::FETCH_ASSOC);

$data = stream_get_contents($row['binarydata']);
$data = pg_unescape_bytea($data);

if($row['gzip']) {
    $data = gzdecode($data);
}

header("Content-type: ".$row['mimetype']."; charset=".$row['charset']);
echo $data;

我必须使用 PDO 对象,我发现的所有示例(甚至在 PHP 网站上)都是基于专用 DBMS API。其次,ByteA 列作为资源返回,然后我不得不使用stream_getcontents() 来获取字符串。当我存储未压缩的文件时,我可以轻松地从中恢复,无论我使用或不使用SET bytea_output = 'escape'; 查询和/或pg_unescape_bytea() 函数。所有组合都允许我获取文件。

当我使用压缩数据时,pg_unescape_bytea() 极大地消耗了我几乎所有的字节。无论如何,在所有组合中,gzdecode() 都无法工作。看来,我的二进制字符串中有缺失或错误的字符,在纯文本模式下不会阻塞。无论如何,这件事在互联网上没有很好的记录,我被困在没有任何线索的地方。

我应该如何使用 PHP PDO 对象恢复存储在 PostgreSQL ByteA 中的 gzcompressed 字符串?

【问题讨论】:

    标签: php postgresql pdo gzip bytea


    【解决方案1】:

    仅解决检索问题,假设插入有效

    一旦二进制内容进入$data,代码如下:

    $data = stream_get_contents($row['binarydata']);
    

    根据需要,它们已经是原始二进制格式,所以你不能用这个再次解码:

    $data = pg_unescape_bytea($data);

    只需删除那个虚假的转义即可。 $data 只有 ASCII 字符时您没有注意到问题的原因是 pg_unescape_bytea 将这些字符转换为它们自己(假设 bytea_output 设置为转义)。

    但是当二进制流确实包含 256 个可能字节的完整范围时,例如在 gzip 压缩的内容中,那么可以保证 pg_unescape_bytea 在该上下文中会产生损坏的结果。

    pg_unescape_bytea 应仅用于直接来自数据库的字符串(作为字节茶编码为文本)。

    通常如何使用 PDO 完成

    实际上,对于 PDO,我们不应该使用 pg_[un]escape_bytea 函数,甚至任何以 pg_* 开头的函数,因为 PDO 是独立于数据库的,它的目的是允许跨不同工作的代码数据库。

    应该按照http://php.net/manual/en/pdo.lobs.php 中的说明进行插入,用PDO::PARAM_LOB 限定二进制参数。执行此操作时,PDO 将自己对数据进行编码以进行二进制传输,并使用与其连接的数据库类型相应的方法。

    当使用pg_escape_bytea() 进行显式转义时,它会生成一个字符串,PDO 可以将其视为文本内容并以此方式传输。这是一种“在 PDO 背后”传输二进制文件的方法,但这样做并没有多大意义。

    绝对行不通的是将两者混合:转义(产生文本)告诉 PDO 它是二进制的。

    【讨论】:

    • 我对此感到非常困惑。我可以看到它链接到字符集。但我不能让它工作。如果不使用 pg_escpae_bytea,我无法将 gzcompressed 数据存储到 ByteA 中,这可能是问题所在。我错过了什么?
    • 插入 gzcompressed without pg_escape_bytea 给我错误:用于编码“UTF8”的无效字节序列:0x8b。当我使用该功能时,检索数据会返回我插入的不同内容。
    • 此页面sickel.net/blogg/?p=1365 提供了一个解决方案,但它适得其反,并且与我想要的工作方式完全相反。我想存储二进制数据(如果不改变 gzip 格式,我就无法做到),而不是二进制数据的十六进制字符串化表示。
    • 我终于找到了解决方案,我编辑了您的问题,然后我可以接受它作为答案。谢谢。
    • @jlandercy:我希望在编辑中澄清。在您的第一条评论中,您似乎更改了插入代码,但我的回答只是关于更改检索,而不是插入。如果你在插入中去掉 pg_escape_bytea,那么你必须改用 PDO::PARAM_LOB 但我认为这就是你最终所做的。
    猜你喜欢
    • 1970-01-01
    • 2021-03-17
    • 2011-06-01
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-05
    相关资源
    最近更新 更多