use*_*851 6 php mysql perl pdo utf-8
代码:
<?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));
Run Code Online (Sandbox Code Playgroud)
简单地删除或以下注释掉线,它会一直工作,不管哪个数据库编码真的使用(utf8,latin1,等):
$pdo->exec('SET CHARACTER SET utf8');
Run Code Online (Sandbox Code Playgroud)
这不是PDO错误,这是MySQL的bug.
当实际的数据库编码是latin1,但你使用:
SET CHARACTER SET utf8
Run Code Online (Sandbox Code Playgroud)
(反之亦然:实际是utf8,但你使用latin1- 重要的是它是不同的),然后,据我所知,MySQL将尝试对客户端和服务器之间的所有流量执行charset转换(即使是BLOB!).
如果你不使用SET CHARACTER SET语句,我从脚本(PHP/PDO或Perl/DBI)看到的连接字符集默认设置为数据库字符集,在这种情况下不会发生隐式转换.
显然,这种自动转换会杀死BLOB,它不希望任何转换发生.
我已经在PHP/PDO和Perl/DBI上对此进行了测试,并且问题很容易重现:如果使用带latin1编码和使用的数据库,则两者都会失败SET CHARACTER SET utf8(反之亦然).
如果要完全UTF8兼容,则应使用以下命令更改数据库的编码:
ALTER DATABASE mydb CHARSET utf8;
Run Code Online (Sandbox Code Playgroud)
有了这个,一切都将使用UTF8,BLOB也将正常工作.
导致此损坏问题的最小文件是blob.bin单字节0xFF.在Linux上,您可以使用以下printf命令创建此测试文件:
printf "0xFF" > blob.bin
Run Code Online (Sandbox Code Playgroud)
现在,测试重现问题的脚本:
<?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;
}
}
}
?>
Run Code Online (Sandbox Code Playgroud)
#!/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";
Run Code Online (Sandbox Code Playgroud)
编辑:在 WAMP 服务器上
它不适用于 PDO API。base64_encode()您可以在插入之前和base64_decode()检索之后使用。它会使数据膨胀,33%并且转换是一种开销。
如果 MySQLi API 是一个选项,那么这里是一些代码:
<?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());
?>
Run Code Online (Sandbox Code Playgroud)