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";
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
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
wypróbowany z plikiem 'dll', również brakowało bajtów. 'mysql_real_escape_string()' również nie jest pomocne. – SparKot