7

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を使用しています

4

4 に答える 4

15

TL;DR:

を削除しaddslashes($data)ます。ここは冗長です。

ダブルエスケープ .. 2 回

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

データを読み取り、文字列リテラルであるかのようにエスケープしてから、バイト 8 進または 16 進エスケープに変換します。pg_escape_bytea正気だったとしても、そのように機能することは決してありません。

PHPは、出力pg_escape_byteaダブルエスケープするように見えるため、文字列リテラルに挿入できます。これは信じられないほど醜いですが、この二重エスケープを行わない代替手段はないようです。そのため、PHP で bytea にパラメーター化されたステートメントを使用できないようです。他のすべてについてもそうする必要があります。

この場合、addslashesファイルから読み込まれたデータの行を削除するだけで十分です。

二重エスケープを示すテストケースpg_escape_bytea(そして常に古い非効率的な 8 進エスケープも使用します):

<?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_byteapg_escape_byteapg_escape_byteapg_query_params

デコード

最新の PostgreSQL を使用している場合、おそらくデフォルトで に設定bytea_outputhexれています。つまり、データを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 のわずかにコンパクトな 16 進表現ですbyteapg_unescape_byteaそれをうまく処理し、出力と同じ生バイトを生成します...最新のPHPとlibpq. 古いバージョンではガベージが発生し、それを処理するには forを設定bytea_outputする必要があります。escapepg_unescape_bytea

代わりにすべきこと

PDO を使用します。

の健全な(ish)サポートがあり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パラメーター化されたステートメントを使用してください。PHPaddslashesは代替手段ではありません。非効率的で醜く、データベース固有のエスケープ規則を理解していません。bytea厄介な歴史的理由で PDO を使用していない場合でも、手動でエスケープする必要がありますが、それ以外はすべてパラメーター化されたステートメントを使用する必要があります。

に関するガイダンスpg_query_params:

于 2013-06-15T09:19:55.997 に答える
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のタイプを使用することを検討してください。を使用すると、テーブルから行を削除すると、関連するラージ オブジェクトが自動的に削除される「孤立した削除」が自動的に行われるため、テーブルの行がラージ オブジェクトを「所有」している場合に適しています。looidlo

画像へのリンクを保存すると、1 つの画像を複数回使用する場合に特に便利です。ただし、データベースから未使用の画像を削除するように注意する必要があります (PHP 関数 pg_lo_unlink())。

postgres ドキュメントのラージ オブジェクト。

PHP マニュアル: pg_lo_import。

于 2013-06-15T21:44:09.257 に答える
0

PDOを使用せずにこれを機能させる奇妙な方法を見つけました。

bytea の代わりに postgresql のテキスト フィールドを使用します。挿入時に、次のようにデータを準備します。

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

次に、クエリの後にファイルを表示する場合は、次を使用します。

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

理由がわかったふりをするつもりはありませんが、これはうまくいきました!

于 2015-09-22T20:29:41.753 に答える