2013-02-06 6 views
5
  1. Mam tabelę z polem BLOB.
  2. Zestaw znaków tabeli to Latin1.
  3. Łączę się z DB i "SET CHARACTER SET utf8".
  4. Następnie zapisuję dane binarne w polu.
  5. Następnie pobieram dane i nie jest to to, co zapisałem (uszkodzone).

Kod:Czy obiekt BLOB jest konwertowany przy użyciu bieżącego/domyślnego zestawu znaków w MySQL?

<?php 
$pdo = new \PDO("mysql:host=127.0.0.1;dbname=***", '***', '***'); 

$pdo->exec('SET CHARACTER SET utf8'); 

$sql = "INSERT INTO pdo_blob (the_blob) VALUES(:the_blob)"; 
$insertStm = $pdo->prepare($sql); 

$blob = (binary) file_get_contents('/home/***/test.pdf'); 
$insertStm->bindParam(":the_blob", $blob, \PDO::PARAM_LOB); 
$insertStm->execute(); 

$selectStm = $pdo->prepare("SELECT the_blob FROM pdo_blob ORDER BY id DESC LIMIT 1"); 
$selectStm->execute(); 

$savedBlob = null; 
$selectStm->bindColumn(1, $savedBlob, \PDO::PARAM_LOB); 
$selectStm->fetch(); 

echo 'equal: ' . ((int) ($blob == $savedBlob)); 
+4

Po pierwsze, pokaż kod, którego używasz do pisania i odczytu BLOB - Myślę, że nie robisz tego całkiem dobrze. Po drugie, BLOB nie powinien mieć wpływu na wybór zestawu znaków. – mvp

+1

Nie mogłem sprawić, żeby to działało; Próbowałem z małym 'favicon.ico'. Niewiele bajtów zostało utraconych po wstawieniu do tabeli. Możesz użyć 'base64_encode()' przed wstawieniem i 'base64_decode()' po pobraniu. Pobiera dane przez "33%", a konwersja to narzut. – SparKot

+1

wypróbowany z plikiem 'dll', również brakowało bajtów. 'mysql_real_escape_string()' również nie jest pomocne. – SparKot

Odpowiedz

1

Dobra odpowiedź @mvp!

Ale kiedy moja aplikacja internetowa jest UTF-8 i kodowanie bazy danych jest latin1, muszęset the character_set_client and character_set_results.

Kiedy używam SET CHARACTER SET utf8, mam opisany problem z BLOBami.

Ale kiedy używam SET NAMES utf8 zamiast tego działa!

+0

To faktycznie rozwiązało problem. – user151851

+0

Nie dla mnie ... Mam ten problem z moją witryną zf2 ... – Amelie

1

Edycja: na WAMP-Server

To nie działa z PDO API. Możesz użyć base64_encode() przed wstawieniem i base64_decode() po pobraniu. Spełnia dane przez 33%, a konwersja to narzut.

Jeśli API MySQLi są możliwe wtedy Oto niektóre kodu:

<?php 
$mysqli = new mysqli('localhost', 'spark', 'spark123', 'test'); 

$sql = "INSERT INTO blob_tb (bdata) VALUES(?)"; 
$insertStm = $mysqli->prepare($sql); 

$blob = NULL; //necessary 
$insertStm->bind_param('b', $blob); 

$blob = (binary) (file_get_contents('favicon.ico')); 
$insertStm->send_long_data(0, $blob); 

$insertStm->execute(); 
$insertStm->close(); 

$selectStm = $mysqli->prepare("SELECT bdata FROM blob_tb LIMIT 1"); 
$selectStm->execute(); 

$selectStm->bind_result($savedBlob); 
$selectStm->fetch(); 
$selectStm->close(); 

$mysqli->close(); 

echo 'equal: ' . ((int) ($blob == $savedBlob)); 
// var_dump(($blob), strlen($blob)); 
// var_dump(($savedBlob), strlen($savedBlob)); 
// var_dump(get_defined_vars()); 

?> 
+0

Dzięki za odpowiedź. To jest droga, ale muszę użyć PDO. Chciałbym wiedzieć, dlaczego tak się dzieje. – user151851

5

Krótka odpowiedź:

Wystarczy usunąć lub zakomentować linię poniżej, a to zawsze działa, bez względu na kodowanie bazy danych jest naprawdę w użyciu (utf8, latin1, etc):

$pdo->exec('SET CHARACTER SET utf8'); 

długi Odpowiedź:

To nie jest błąd PDO, to błąd MySQL.

Kiedy rzeczywisty kodowanie bazy danych jest latin1, ale użyć:

SET CHARACTER SET utf8 

(lub odwrotnie: rzeczywista jest utf8, ale użyć latin1 - ważne jest to, że jest inny), a następnie, jak o ile mogę powiedzieć, MySQL spróbuje przeprowadzić konwersję charset dla całego ruchu pomiędzy klientem a serwerem (nawet dla BLOB!).

Jeśli NIE używasz instrukcji SET CHARACTER SET, z tego, co widzę dla skryptów (PHP/PDO lub Perl/DBI) zestaw znaków połączenia domyślnie jest ustawiany jako zestaw znaków do bazy danych, i w takim przypadku nie następuje żadna niejawna konwersja.

Oczywiście ta automatyczna konwersja zabija BLOBy, które nie chcą żadnej konwersji.

Testowałem to zarówno na PHP/PDO, jak i na Perl/DBI, a problem jest łatwo powtarzalny: oba zawodzą, jeśli używa się bazy danych z kodowaniem latin1 i używają SET CHARACTER SET utf8 (lub odwrotnie).

Jeśli chcesz być w pełni kompatybilny UTF8, należy zmienić kodowanie bazy danych przy użyciu:

ALTER DATABASE mydb CHARSET utf8; 

z tym, wszystko będzie za pomocą UTF8 i BLOB będzie również działać dobrze.

Minimalny plik powodujący ten problem z korupcją to blob.bin z pojedynczym bajtem 0xFF. W systemie Linux, można utworzyć ten plik testowy używając printf polecenie:

printf "0xFF" > blob.bin 

Teraz skrypty testowe, które rozmnażają się problem:

kod Test PHP:

<?php 
$dbh = new PDO("mysql:host=127.0.0.1;dbname=test"); 
# If database encoding is NOT utf8, uncomment to break it: 
# $dbh->exec("SET CHARACTER SET utf8"); 

$blob1 = file_get_contents("blob.bin"); 
$sth = $dbh->prepare(
    "INSERT INTO pdo_blob (the_blob) VALUES(:the_blob)" 
); 
$sth->bindParam(":the_blob", $blob1, PDO::PARAM_LOB); 
$sth->execute(); 

$sth = $dbh->prepare(
    "SELECT the_blob FROM pdo_blob ORDER BY id DESC LIMIT 1" 
); 
$sth->execute(); 

$blob2 = null; 
$sth->bindColumn(1, $blob2, PDO::PARAM_LOB); 
$sth->fetch(); 

if ($blob1 == $blob2) { 
    echo "Equal\n"; 
} else { 
    echo "Not equal\n"; 
    $arr1 = str_split($blob1); 
    $arr2 = str_split($blob2); 
    $i=0; 
    for ($i=0; $i<count($arr1); $i++) { 
     if ($arr1[$i] != $arr2[$i]) { 
      echo "First diff: " . dechex(ord($arr1[$i])) . " != " 
           . dechex(ord($arr2[$i])) . "\n"; 
      break; 
     } 
    } 
} 
?> 

Perl kodu testu:

#!/usr/bin/perl -w 

use strict; 
use DBI qw(:sql_types); 

my $dbh = DBI->connect("dbi:mysql:host=127.0.0.1;dbname=test"); 
# If database encoding is NOT utf8, uncomment to break it: 
# $dbh->do("SET CHARACTER SET utf8"); 
open FILE, "blob.bin"; 
binmode FILE; 
read(FILE, my $blob1, 100000000); 
close FILE; 
my $sth = $dbh->prepare(
    "INSERT INTO pdo_blob (the_blob) VALUES(?)" 
); 
$sth->bind_param(1, $blob1, SQL_BLOB); 
$sth->execute(); 
my ($blob2) = $dbh->selectrow_array(
    "SELECT the_blob FROM pdo_blob ORDER BY id DESC LIMIT 1" 
); 
print ($blob1 eq $blob2 ? "Equal" : "Not equal") , "\n"; 
Powiązane problemy