是否使用MySQL中的当前/默认字符集转换BLOB?

时间:2013-02-06 17:09:35

标签: php mysql perl utf-8 pdo

  1. 我有一个带有BLOB字段的表。
  2. 该表的字符集是Latin1。
  3. 我连接到DB和“SET CHARACTER SET utf8”。
  4. 然后我将二进制数据保存到字段中。
  5. 然后我检索数据,这不是我保存的(损坏的)。
  6. 代码:

    <?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));
    

3 个答案:

答案 0 :(得分:6)

简答:

只需删除或注释下面的行,无论使用哪种数据库编码(utf8latin1等),它都将始终有效:

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

长答案:

这不是PDO错误,这是MySQL的错误。

当实际数据库编码为latin1时,您使用:

SET CHARACTER SET utf8

(反之亦然:实际为utf8,但您使用的是latin1 - 重要的一部分是不同),据我所知, MySQL将尝试对客户端和服务器之间的所有流量执行字符集转换(即使对于BLOB!)。

如果你不使用SET CHARACTER SET语句,从我看到的脚本(PHP / PDO或Perl / DBI),默认情况下连接字符集被设置为数据库字符集,在这种情况下,没有隐式转换需要的地方。

显然,这种自动转换会杀死BLOB,它不希望任何转换发生。

我已经在PHP / PDO和Perl / DBI上对此进行了测试,问题很容易重现:如果使用latin1编码的数据库并使用SET CHARACTER SET utf8(反之亦然),则两者都将失败。 / p>

如果要完全兼容UTF8,则应使用以下命令更改数据库的编码:

ALTER DATABASE mydb CHARSET utf8;

有了这个,一切都将使用UTF8,BLOB也可以正常工作。

导致此损坏问题的最小文件是blob.bin,其中包含单字节0xFF。在Linux上,您可以使用printf命令创建此测试文件:

printf "0xFF" > blob.bin

现在,测试重现问题的脚本:

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测试代码:

#!/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";

答案 1 :(得分:1)

编辑:在WAMP-Server上

它无法与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());

?>

答案 2 :(得分:1)

答案很好@mvp!

但是当我的网络应用是UTF-8且数据库编码为latin1时,我必须 set the character_set_client and character_set_results

当我使用SET CHARACTER SET utf8时,我得到了BLOB的描述问题。

但是,当我使用SET NAMES utf8时,它会起作用!