我使用PHP将图像存储在列类型为bytea的PostgreSQL数据库中。问题是每次我尝试在浏览器中加载图像时都不会出现。 Firefox开发人员控制台表示图像被截断或损坏。
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]);
我需要将上传的图像存储在数据库中 - 我实际上正在使用heroku谢谢
答案 0 :(得分:10)
删除addslashes($data)
。这里多余。
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data);
您读取数据,将其转义为字符串文字,然后将其转换为bytea八进制或十六进制转义。即使pg_escape_bytea
是理智的,它也可能永远不会那样,但事实并非如此。
PHP pg_escape_bytea
似乎双重转义输出,因此可以将其插入到字符串文字中。这非常难看,但似乎没有一个替代方法不会进行双重转义,所以你似乎无法在PHP中使用参数化语句来实现bytea。你还应该为其他一切做到这一点。
在这种情况下,只需从文件中删除读入数据的addslashes
行即可。
测试用例显示pg_escape_bytea
双重转义(并且总是使用旧的,低效的八进制转义):
<?php
# oh-the-horror.php
print pg_escape_bytea("Blah binary\x00\x01\x02\x03\x04 blah");
?>
执行命令
php oh-the-horror.php
结果:
Blah binary\\000\\001\\002\\003\\004 blah
看到加倍的反斜杠?那是因为它假设你要将它作为一个字符串插入到SQL中,这是一个非常低效,丑陋和非常糟糕的习惯。但是,你似乎没有任何其他选择。
除此之外,这意味着:
pg_unescape_bytea(pg_escape_bytea("\x01\x02\x03"));
...生成错误结果,因为pg_unescape_bytea
实际上并不是pg_escape_bytea
的反转。这也使得无法将pg_escape_bytea
的输出作为参数提供给pg_query_params
,您必须将其插入。
如果您使用的是现代PostgreSQL,则默认情况下可能会将bytea_output
设置为hex
。这意味着如果我将数据写入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)
你可能会说“嗯,什么”?这很好,它只是PostgreSQL的bytea
的更紧凑的十六进制表示。 pg_unescape_bytea
将处理它并产生与输出相同的原始字节...如果你有一个现代的PHP和libpq
。在旧版本上,您将获得垃圾,并且需要将bytea_output
设置为escape
pg_unescape_bytea
来处理它。
使用PDO。
对bytea
有明智的支持。
$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();
请参阅:
您可能还想查看PostgreSQL的lob(大对象)支持,它提供了一个仍然完全事务性的流式可搜索接口。
如果PHP在“字节字符串”和“文本字符串”类型之间存在真正的区别,您甚至不需要pg_escape_bytea
,因为数据库驱动程序可以为您执行此操作。这些丑陋都不是必需的。不幸的是,PHP中没有单独的字符串和字节类型。
请尽可能使用带参数化语句的PDO。
你不能,至少使用pg_query_params
和参数化语句。 PHP addslashes
不是替代品,它效率低,难看,并且不了解数据库特定的转义规则。如果你因为icky历史原因没有使用PDO,你仍然需要手动转义bytea
,但其他一切都应该通过参数化语句。
有关pg_query_params
的指导:
答案 1 :(得分:3)
如果您真的必须在数据库中存储图像,最好使用postgres 大对象。在 userinfo 表中,而不是图片本身只存储指向 loid (大对象ID)的链接。
将图像插入数据库:
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");
从数据库中检索图像:
$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");
loid 字段属于oid类型(当然您可以根据需要为其命名)。
请考虑使用lo
类型from the lo
extension,而不要使用oid
类型。使用lo
为您提供自动“孤立删除”,从表中删除行将自动删除关联的大对象,因此这对于表行“拥有”大对象的情况很有用。
如果您多次使用一张图像,则存储图像链接尤其方便。但是,您应该注意从数据库中删除未使用的图像(PHP函数pg_lo_unlink())。
答案 2 :(得分:0)
我发现了一种在不使用PDO的情况下让它工作的奇怪方法。
在postgresql中使用文本字段而不是bytea。 在插入时,准备好您的数据:
$imgdta = pg_escape_string(bin2hex($filedata));
然后,当您想在查询后显示文件时,请使用:
echo pack("H*", $img["filedata"]);
我不会假装我明白为什么,但这对我有用!
答案 3 :(得分:0)
由于您的数据来源是文件系统中的文件,因此我觉得找到灵感有效here:
在您的数据库中创建一个辅助函数,以超级用户身份运行:
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;
在你的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'");