trả lời ngắn:
Đơn giản chỉ cần xóa hoặc nhận xét ra dòng dưới đây, và nó sẽ luôn luôn làm việc, cho dù mã hóa cơ sở dữ liệu là thực sự được sử dụng (utf8
, latin1
, vv):
$pdo->exec('SET CHARACTER SET utf8');
dài trả lời:
Đây không phải là lỗi PDO, đây là lỗi của MySQL.
Khi mã hóa cơ sở dữ liệu thực tế là latin1
, nhưng bạn sử dụng:
SET CHARACTER SET utf8
(hoặc ngược lại: thực tế là utf8
, nhưng bạn sử dụng latin1
- phần quan trọng là nó là khác nhau), sau đó, như xa như tôi có thể nói, MySQL sẽ cố gắng thực hiện chuyển đổi ký tự cho tất cả lưu lượng truy cập giữa máy khách và máy chủ (ngay cả đối với BLOB
!).
Nếu bạn KHÔNG sử dụng câu lệnh SET CHARACTER SET
, từ bộ mã kết nối của tập lệnh (PHP/PDO hoặc Perl/DBI) theo mặc định được đặt làm bộ ký tự cơ sở dữ liệu và trong trường hợp đó không xảy ra chuyển đổi ẩn.
Rõ ràng, chuyển đổi tự động này là những gì tiêu diệt BLOB, không muốn bất kỳ chuyển đổi nào xảy ra.
Tôi đã thử nghiệm điều này trên cả PHP/PDO và Perl/DBI, và vấn đề có thể dễ dàng tái sản xuất: cả hai sẽ thất bại nếu sử dụng cơ sở dữ liệu với mã hóa latin1
và sử dụng SET CHARACTER SET utf8
(hoặc ngược lại).
Nếu bạn muốn trở thành hoàn toàn UTF8
tương thích, bạn nên thay đổi mã hóa của cơ sở dữ liệu bằng cách sử dụng:
ALTER DATABASE mydb CHARSET utf8;
Với điều này, mọi thứ sẽ sử dụng UTF8
, và các đốm màu cũng sẽ hoạt động tốt.
Tệp tối thiểu gây ra sự cố tham nhũng này là blob.bin
với một byte 0xFF
. Trên Linux, bạn có thể tạo tập tin thử nghiệm này sử dụng printf
lệnh:
printf "0xFF" > blob.bin
Bây giờ, kịch bản thử nghiệm mà tạo lại vấn đề:
mã kiểm tra 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 mã kiểm tra:
#!/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";
Trước tiên, vui lòng hiển thị mã mà bạn sử dụng để viết và đọc BLOB của mình - Tôi nghĩ bạn không làm điều đó hoàn toàn đúng. Thứ hai, BLOB không nên bị ảnh hưởng bởi bất kỳ sự lựa chọn ký tự nào. – mvp
Tôi không thể làm cho nó hoạt động; Tôi đã thử với một 'favicon.ico' nhỏ. Thiếu vài byte khi được chèn vào bảng. Bạn có thể sử dụng 'base64_encode()' trước khi chèn và 'base64_decode()' sau khi truy xuất. Nó bloats dữ liệu bằng '33%' và chuyển đổi là một chi phí. – SparKot
đã thử với tệp 'dll', byte cũng bị thiếu ở đây. 'mysql_real_escape_string()' cũng không phải là bất kỳ trợ giúp nào. – SparKot