Хранение изображений в байтовых полях в базе данных PostgreSQL

Я сохранил изображение в базе данных PostgreSQL с байтом типа столбца, используя PHP. Проблема заключается в том, что каждый раз, когда я пытаюсь загрузить изображение в браузере, он не появляется. Консоль разработчика 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]); 

и мне нужно сохранить загруженное изображение в базе данных – я действительно использую герою спасибо

TL; DR:

Удалить addslashes($data) . Здесь избыточно.

Двойное ускорение .. дважды

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

Вы читаете данные, избегаете его, как если бы это был строковый литерал, а затем преобразовывали его в восьмеричные или шестнадцатеричные экраны bytea. Это никогда не могло работать так, даже если pg_escape_bytea был нормальным, а это не так.

PHP pg_escape_bytea похоже, удваивает выход, поэтому его можно вставить в строковый литерал. Это невероятно уродливо, но, похоже, нет альтернативы, которая не делает этого двойного экранирования, поэтому вы не можете использовать параметризованные инструкции для bytea в PHP. Вы все равно должны это делать для всего остального.

В этом случае достаточно просто удалить строку 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) 

«Хм, что», можно сказать? Все в порядке, это просто более компактное шестнадцатеричное представление bytea . pg_unescape_bytea справится с этим и создаст те же самые сырые байты, что и вывод … если у вас есть современный PHP и libpq . В более старых версиях вы получите мусор и вам нужно будет установить bytea_output для pg_unescape_bytea на pg_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(); 

Видеть:

  • PHP: крупные объекты , которые имеют пример именно того, что вы хотите;
  • PDOStatement :: bindParam
  • как хранить сериализованный объект с пространством имен в базе данных с помощью pdo php
  • Привязать BYTEA к PGSQL PDO Подготовленный отчет в PHP5

Вы также можете посмотреть в поддержку lob (большой объект) PostgreSQL, которая обеспечивает потоковый, доступный для просмотра интерфейс, который по-прежнему полностью транзакционный.

Теперь, на мой ящик для мыла

Если бы PHP имело реальное различие между типами «байтовая строка» и «текстовая строка», вам даже не понадобилось бы pg_escape_bytea поскольку драйвер базы данных мог бы сделать это за вас. Ничто из этого уродства не потребовалось бы. К сожалению, в PHP нет отдельных типов строк и байтов.

Пожалуйста, используйте PDO с параметризованными инструкциями как можно больше.

Если вы не можете, используйте, по крайней мере, pg_query_params и параметризованные операторы. addslashes PHP не являются альтернативой, они неэффективны, уродливы и не понимают правила экранирования, специфичные для конкретной базы данных. Вам все равно придется вручную бежать bytea если вы не используете PDO по нечетным историческим причинам, но все остальное должно проходить через параметризованные утверждения.

Для руководства по pg_query_params :

  • Таблицы Бобби , раздел PHP.
  • Руководство по PHP на pg_query_params

Лучше использовать большие объекты postgres, если вам действительно нужно хранить изображения в вашей базе данных. В таблице userinfo вместо самого изображения хранится только ссылка на него как loid (большой идентификатор объекта).

Вставьте изображение в базу данных:

  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 из расширения lo вместо использования типа oid . Использование lo дает вам автоматическое «удаление сироты», при котором удаление строки из таблицы автоматически удаляет связанный большой объект, поэтому это хорошо для случаев, когда строка таблицы «владеет» большим объектом.

Сохранение ссылок на изображения особенно удобно, если вы используете одно изображение более одного раза. Однако вы должны обратить внимание на удаление неиспользуемых изображений из вашей базы данных (функция PHP pg_lo_unlink ()).

Большие объекты в документации postgres.

Руководство по PHP: pg_lo_import.

Я нашел странный способ заставить это работать, не используя PDO.

Используйте текстовое поле в postgresql вместо bytea. На вставке подготовьте свои данные следующим образом:

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

Затем, когда вы хотите отобразить файл после запроса, используйте:

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

Я не собираюсь притворяться, что понимаю почему, но это сработало для меня!