2013-02-06 29 views
5
  1. Tôi có một bảng có trường BLOB.
  2. Bộ ký tự của bảng là Latin1.
  3. Tôi kết nối với DB và "SET CHARACTER SET utf8".
  4. Sau đó, tôi lưu dữ liệu nhị phân vào trường.
  5. Sau đó, tôi truy xuất dữ liệu và không phải là dữ liệu tôi đã lưu (bị hỏng).

Mã:BLOB có được chuyển đổi bằng bộ ký tự hiện tại/mặc định trong MySQL không?

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

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

+1

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

+1

đã 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

Trả lời

1

Câu trả lời hay @mvp!

Nhưng khi ứng dụng web của tôi là UTF-8 và mã hóa cơ sở dữ liệu là latin1, I phảiset the character_set_client and character_set_results.

Khi tôi sử dụng SET CHARACTER SET utf8, tôi nhận được sự cố được mô tả với BLOB.

Nhưng khi tôi sử dụng SET NAMES utf8 thay vào đó, nó hoạt động!

+0

Điều đó thực sự đã giải quyết được vấn đề. – user151851

+0

Không phải cho tôi ... tôi có vấn đề này với trang web zf2 của tôi ... – Amelie

1

Chỉnh sửa: trên wamp-Server

Nó đã không làm việc với PDO API. 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 của 33% và chuyển đổi là một chi phí.

Nếu MySQLi API là một lựa chọn thì đây là một số mã:

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

?> 
+0

Cảm ơn câu trả lời. Đó là một cách để đi nhưng tôi cần phải sử dụng PDO. Tôi cũng muốn biết tại sao nó lại xảy ra. – user151851

5

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"; 
Các vấn đề liên quan