7
  1. BLOB フィールドを持つテーブルがあります。
  2. テーブルの文字セットは Latin1 です。
  3. DBと「SET CHARACTER SET utf8」に接続します。
  4. 次に、バイナリ データをフィールドに保存します。
  5. 次に、データを取得しましたが、保存したものではありません (破損しています)。

コード:

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

2 に答える 2

7

簡潔な答え:

以下の行を削除またはコメントアウトするだけで、実際に使用されているデータベースエンコーディング ( 、 など) に関係なく、常に機能しutf8ますlatin1

$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(またはその逆) で使用すると、どちらも失敗します。

完全に互換性を持たせたい場合はUTF8、次を使用してデータベースのエンコーディングを変更する必要があります。

ALTER DATABASE mydb CHARSET utf8;

これにより、すべてが を使用しUTF8、BLOB も正常に動作します。

この破損の問題を引き起こす最小のファイルはblob.bin、シングル バイト0xFFです。printfLinux では、次のコマンドを使用してこのテスト ファイルを作成できます。

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";
于 2013-02-07T07:17:53.830 に答える
0

編集: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());

?>
于 2013-02-06T19:29:21.243 に答える