PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Speichern von Bildern in Bytea-Feldern in einer PostgreSQL-Datenbank

TL;DR:

Löschen Sie addslashes($data) . Es ist hier überflüssig.

Double-Escape .. zweimal

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

Sie lesen die Daten ein, maskieren sie, als wären sie ein String-Literal, und konvertieren sie dann in Bytea-Oktal- oder Hex-Escapes. So herum könnte es nie funktionieren, selbst wenn pg_escape_bytea war vernünftig, was es nicht ist.

pg_escape_bytea von PHP scheint doppelt zu entkommen die Ausgabe, damit sie in ein Zeichenfolgenliteral eingefügt werden kann. Das ist unglaublich hässlich, aber es scheint keine Alternative zu geben, die dieses Double-Escape nicht macht, also können Sie scheinbar keine parametrisierten Anweisungen für bytea in PHP verwenden. Für alles andere sollten Sie es trotzdem tun.

Entfernen Sie in diesem Fall einfach die addslashes Zeile für die aus der Datei eingelesenen Daten genügt.

Testfall, der diesen pg_escape_bytea zeigt Double-Escapes (und verwendet auch immer die alten, ineffizienten Oktal-Escapes):

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

Ausführen:

php oh-the-horror.php

Ergebnis:

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

Sehen Sie die doppelten Backslashes? Das liegt daran, dass davon ausgegangen wird, dass Sie es als Zeichenfolge in SQL interpolieren, was extrem speicherineffizient, hässlich und eine sehr schlechte Angewohnheit ist. Du scheinst aber keine Alternative zu bekommen.

Das bedeutet unter anderem:

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

... liefert das falsche Ergebnis , seit pg_unescape_bytea ist eigentlich nicht das Gegenteil von pg_escape_bytea . Es macht es auch unmöglich, die Ausgabe von pg_escape_bytea zu füttern in pg_query_params als Parameter müssen Sie ihn interpolieren.

Decodierung

Wenn Sie ein modernes PostgreSQL verwenden, setzt es wahrscheinlich bytea_output zu hex standardmäßig. Das heißt, wenn ich meine Daten in ein bytea schreibe Feld dann zurückholen, sieht es etwa so aus:

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)

"Ähm, was", könntest du sagen? Es ist in Ordnung, es ist nur PostgreSQLs etwas kompaktere Hex-Darstellung von bytea . pg_unescape_bytea wird damit gut umgehen und die gleichen rohen Bytes als Ausgabe erzeugen ... wenn Sie ein modernes PHP und libpq haben . Bei älteren Versionen erhalten Sie Datenmüll und müssen bytea_output setzen um escape für pg_unescape_bytea um damit umzugehen.

Was Sie stattdessen tun sollten

PDO verwenden.

Es hat vernünftige Unterstützung für 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();

Siehe:

  • PHP:Large Objects, das ein Beispiel für genau das hat, was Sie wollen;
  • PDOStatement::bindParam
  • wie man serialisierte Objekte mit Namespace in der Datenbank mit pdo php speichert
  • Binden Sie BYTEA an die vorbereitete PGSQL-PDO-Anweisung in PHP5

Vielleicht möchten Sie auch einen Blick auf die lob (Large Object)-Unterstützung von PostgreSQL werfen, die eine durchsuchbare Streaming-Schnittstelle bietet, die immer noch vollständig transaktional ist.

Nun zu meiner Seifenkiste

Wenn PHP wirklich zwischen den Typen "Byte-String" und "Text-String" unterscheiden würde, bräuchten Sie nicht einmal pg_escape_bytea wie der Datenbanktreiber es für Sie tun könnte. Nichts von dieser Hässlichkeit wäre erforderlich. Leider gibt es in PHP keine separaten String- und Byte-Typen.

Bitte verwenden Sie so viel wie möglich PDO mit parametrisierten Anweisungen.

Wo Sie es nicht können, verwenden Sie zumindest pg_query_params und parametrisierte Anweisungen. PHPs addslashes ist keine Alternative, es ist ineffizient, hässlich und versteht keine datenbankspezifischen Escape-Regeln. Sie müssen bytea immer noch manuell maskieren wenn Sie PDO aus ekligen historischen Gründen nicht verwenden, aber alles andere durch parametrisierte Anweisungen gehen sollte.

Für Anleitungen zu pg_query_params :

  • Bobby-Tabellen, PHP-Abschnitt.
  • Das PHP-Handbuch zu pg_query_params