简体   繁体   English

将图像存储在PostgreSQL数据库的bytea字段中

[英]Storing images in bytea fields in a PostgreSQL database

I stored an image in a PostgreSQL database with column type bytea using PHP. 我使用PHP将图像存储在具有列类型bytea的PostgreSQL数据库中。 The problem is every time I try to load the image in a browser it does not appear. 问题是每次我尝试在浏览器中加载图像时都不会出现。 The Firefox developer console says the image is either truncated or corrupt. Firefox开发人员控制台表示图像被截断或损坏。

The PHP code: PHP代码:

//code for inserting into the database
if(array_key_exists('submit_pic', $_POST)){
$user=$_SESSION['name'];
if(isset($_FILES['thumbnail'])&&$_FILES['thumbnail']['size']>0){
$fi =  $_FILES['thumbnail']['tmp_name'];
$p=fopen($fi,'r');
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data); 
$q="update userinfo set image='{$dat}' where email='$user'";
$e=pg_query($q)or die(pg_last_error());

// code for retreving from database
require_once('conn.php');
session_start();
$user=$_SESSION['name'];
pg_query('SET bytea_output = "escape";');
$lquery ="select image from userinfo where email='$user'";
$lq = pg_query($lquery)or die(pg_last_error());
$lqq=pg_fetch_row($lq,'image');
header("conent-type:image");
echo pg_unescape_bytea($lqq[0]);

and i need to store the uploaded image in a database- i am actually using heroku thanks 我需要将上传的图像存储在数据库中 - 我实际上正在使用heroku谢谢

TL;DR: TL; DR:

Delete addslashes($data) . 删除addslashes($data) It's redundant here. 这里多余。

Double-escaping .. twice 双重逃避..两次

$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data); 

You read the data in, escape it as if it were a string literal, then convert it to bytea octal or hex escapes. 您读取数据,将其转义为字符串文字,然后将其转换为bytea八进制或十六进制转义。 It could never work that way around even if pg_escape_bytea was sane, which it isn't. 即使pg_escape_bytea是理智的,它也不会那样,但事实并非如此。

PHP's pg_escape_bytea appears to double-escape the output so it can be inserted into a string literal. PHP的pg_escape_bytea似乎双重转义输出,因此可以将其插入到字符串文字中。 This is incredibly ugly, but there doesn't appear to be an alternative that doesn't do this double-escaping, so you can't seem to use parameterised statements for bytea in PHP. 这非常难看,但似乎没有一个替代方法不会进行双重转义,所以你似乎无法在PHP中使用参数化语句来实现bytea。 You should still do so for everything else. 你还应该为其他一切做到这一点。

In this case, simply removing the addslashes line for the data read in from the file is sufficient. 在这种情况下,只需删除从文件读入的数据的addslashes行就足够了。

Test case showing that pg_escape_bytea double-escapes (and always uses the old, inefficient octal escapes, too): 测试用例显示pg_escape_bytea双重转义(并且总是使用旧的,低效的八进制转义):

<?php
# oh-the-horror.php
print pg_escape_bytea("Blah binary\x00\x01\x02\x03\x04 blah");
?>

Run: 跑:

php oh-the-horror.php

Result: 结果:

Blah binary\\000\\001\\002\\003\\004 blah

See the doubled backslashes? 看到加倍的反斜杠? That's because it's assuming you're going to interpolate it into SQL as a string, which is extremely memory inefficient, ugly, and a very bad habit. 那是因为它假设你要将它作为一个字符串插入到SQL中,这是一个非常低效,丑陋和非常糟糕的习惯。 You don't seem to get any alternative, though. 但是,你似乎没有任何其他选择。

Among other things this means that: 除此之外,这意味着:

pg_unescape_bytea(pg_escape_bytea("\x01\x02\x03"));

... produces the wrong result , since pg_unescape_bytea is not actually the reverse of pg_escape_bytea . ...产生错误的结果 ,因为pg_escape_bytea实际上与pg_unescape_bytea不相反。 It also makes it impossible to feed the output of pg_escape_bytea into pg_query_params as a parameter, you have to interpolate it in. 它也使得无法将pg_escape_bytea的输出作为参数提供给pg_query_params ,您必须将其插入。

Decoding 解码

If you're using a modern PostgreSQL, it probably sets bytea_output to hex by default. 如果你使用的是现代的PostgreSQL,它可能会默认将bytea_output设置为hex That means that if I write my data to a bytea field then fetch it back, it'll look something like this: 这意味着如果我将数据写入bytea字段然后将其取回,它将看起来像这样:

craig=> CREATE TABLE byteademo(x bytea);
CREATE TABLE
craig=> INSERT INTO byteademo(x) VALUES ('Blah binary\\000\\001\\002\\003\\004 blah');
INSERT 0 1
craig=> SELECT * FROM byteademo ;
                                     x                                      
----------------------------------------------------------------------------
 \x426c61682062696e6172795c3030305c3030315c3030325c3030335c30303420626c6168
(1 row)

"Um, what", you might say? “嗯,什么”,你可能会说? It's fine, it's just PostgreSQL's slightly more compact hex representation of bytea . 这很好,它只是PostgreSQL稍微更紧凑的bytea十六进制表示。 pg_unescape_bytea will handle it fine and produce the same raw bytes as output ... if you have a modern PHP and libpq . pg_unescape_bytea将处理它并产生与输出相同的原始字节...如果你有一个现代的PHP和libpq On older versions you'll get garbage and will need to set bytea_output to escape for pg_unescape_bytea to handle it. 在老版本中,你会得到垃圾和需要设置bytea_outputescapepg_unescape_bytea来处理它。

What you should do instead 你应该做什么

Use PDO. 使用PDO。

It has sane(ish) support for bytea . 它对bytea有理智(ish)支持。

$sth = $pdo->prepare('INSERT INTO mytable(somecol, byteacol) VALUES (:somecol, :byteacol)');
$sth->bindParam(':somecol', 'bork bork bork');
$sth->bindParam(':byteacol', $thebytes, PDO::PARAM_LOB);
$sth->execute();

See: 看到:

You may also want to look in to PostgreSQL's lob (large object) support, which provides a streaming, seekable interface that's still fully transactional. 您可能还想查看PostgreSQL的lob(大对象)支持,它提供了一个仍然完全事务性的流式可搜索接口。

Now, on to my soap box 现在,到我的肥皂盒

If PHP had a real distinction between "byte string" and "text string" types, you wouldn't even need pg_escape_bytea as the database driver could do it for you. 如果PHP在“字节字符串”和“文本字符串”类型之间存在真正的区别,那么您甚至不需要pg_escape_bytea因为数据库驱动程序可以为您执行此操作。 None of this ugliness would be required. 这些丑陋都不是必需的。 Unfortunately, there are no separate string and bytes types in PHP. 不幸的是,PHP中没有单独的字符串和字节类型。

Please, use PDO with parameterised statements as much as possible. 请尽可能使用PDO和参数化语句。

Where you can't, at least use pg_query_params and parameterised statements. 在你不能的地方,至少使用pg_query_params和参数化语句。 PHP's addslashes is not an alternative, it's inefficient, ugly, and doesn't understand database specific escaping rules. PHP的addslashes不是替代品,它效率低,难看,并且不了解数据库特定的转义规则。 You still have to manually escape bytea if you're not using PDO for icky historical reasons, but everything else should go through parameterised statements. 如果你因为icky历史原因没有使用PDO,你仍然需要手动转义bytea ,但其他一切都应该通过参数化语句。

For guidance on pg_query_params : 有关pg_query_params指导:

It is better to use postgres large objects if you really have to store images in your database. 如果你真的必须在数据库中存储图像,最好使用postgres 大对象 In the userinfo table instead of the image itself store just a link to it as loid (large object id). userinfo表而不是图像本身只存储一个链接作为loid (大对象id)。

Insert an image into the database: 将图像插入数据库:

    pg_query("begin");  // pg_lo functions need to be run in a transaction
    $loid = pg_lo_import('full_path_and_file_name');
    pg_query("update userinfo set loid=$loid where email='$user'");
    pg_query("commit");

Retrieve an image from the database: 从数据库中检索图像:

    $rs = pg_query("select loid from userinfo where email='$user'");
    $loid = pg_fetch_row($rs, 0)[0];
    pg_query("begin");
    $blob = pg_lo_open($loid, "r");
    header("Content-type: image");
    pg_lo_read_all($blob);
    pg_lo_close($blob);
    pg_query("commit");

The loid field is of type oid (of course you can name it as you want). loid字段是oid类型(当然你可以根据需要命名)。

Consider using the lo type from the lo extension instead of using the oid type. 考虑使用lo扩展中lo类型而不是使用oid类型。 Using lo gives you automatic "orphan removal", where deleting a row from a table will automatically remove the associated large object, so it's good for cases where a table row "owns" a large object. 使用lo为您提供自动“孤立删除”,从表中删除行将自动删除关联的大对象,因此它适用于表行“拥有”大对象的情况。

Storing links to images is especially convenient in case you use one image more than one time. 如果您多次使用一个图像,则存储图像链接特别方便。 However, you should pay attention to delete unused images from your database (PHP function pg_lo_unlink()). 但是,您应该注意从数据库中删除未使用的图像(PHP函数pg_lo_unlink())。

Large objects in postgres documentation. postgres文档中的大对象。

PHP manual: pg_lo_import. PHP手册:pg_lo_import。

I found a strange way of getting this to work too without using PDO. 在没有使用PDO的情况下,我发现了一种让它工作的奇怪方法。

Use a text field in postgresql instead of bytea. 在postgresql中使用文本字段而不是bytea。 On insert, prep your data like this: 在插入时,准备您的数据,如下所示:

$imgdta = pg_escape_string(bin2hex($filedata));

Then when you want to display the file after your query, use: 然后,当您想在查询后显示文件时,请使用:

echo pack("H*", $img["filedata"]);

I'm not going to pretend I get why, but this worked for me! 我不会假装我明白为什么,但这对我有用!

As the source of your data is a file in the file system so it seems to me efficient to find an inspiration here : 作为您的数据源是在文件系统中的文件,因此在我看来,有效的找到一个灵感在这里

In your db create an auxiliary function, run as superuser: 在您的数据库中创建一个辅助函数,以超级用户身份运行:

create or replace function bytea_import(p_path text, p_result out bytea) 
                   language plpgsql as $$
declare
  l_oid oid;
begin
  select lo_import(p_path) into l_oid;
  select lo_get(l_oid) INTO p_result;
  perform lo_unlink(l_oid);
end;$$
security definer;

In your php execute a query like: 在您的PHP中执行如下查询:

#make sure that postgres will have access to the file
chmod($_FILES['thumbnail']['tmp_name'], 0644);
pg_query("update userinfo set image=(select bytea_import('".$_FILES['thumbnail']['tmp_name']."')) where email='$user'");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM