PHP / PDO / MySQL: вставка в MEDIUMBLOB хранит неверные данные - PullRequest
3 голосов
/ 14 июня 2011

У меня есть простое веб-приложение на PHP, которое принимает изображения значков посредством загрузки файлов и сохраняет их в столбце MEDIUMBLOB.

На моей машине (Windows) плюс два сервера Linux это работает нормально. На третьем сервере Linux вставленное изображение повреждено: не читается после SELECT, а длина данных столбца, сообщаемых функцией MySQL length (), примерно на 40% больше, чем размер загруженного файла.

(Каждый сервер подключается к отдельному экземпляру MySQL.)

Конечно, это заставляет меня задуматься о проблемах кодирования и набора символов. Столбцы BLOB не имеют связанных кодировок, поэтому наиболее вероятным виновником является PDO и его интерпретация значения параметра для этого столбца.

  • Я пытался использовать bindValue с PDO :: PARAM_LOB, но безрезультатно.
  • Я проверил, что изображения принимаются на сервер правильно (т.е. я читаю их после загрузки без проблем), так что это определенно проблема с DB / PDO.
  • Я искал очевидные различия в конфигурации между серверами, но я не эксперт в конфигурации PHP, поэтому я мог что-то пропустить.

Код вставки выглядит примерно так:

$imagedata = file_get_contents($_FILES["icon"]["tmp_name"]);
$stmt = $pdo->prepare('insert into foo (theimage) values (:theimage)');
$stmt->bindValue(':theimage', $imagedata, PDO::PARAM_LOB);
$stmt->execute();

Любая помощь будет по достоинству оценена.

ОБНОВЛЕНИЕ : кодировка MySQL по умолчанию на проблемном сервере - utf8; это латынь1 на других.

Проблема «решается» путем добавления PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES latin1 COLLATE latin1_general_ci" в конструктор PDO.

Мне кажется, ошибка плохой дизайн: почему кодировка соединения должна иметь какое-либо влияние на данные для двоичного столбца, особенно когда он идентифицирован как двоичный для Сам PDO с PARAM_LOB?

Обратите внимание, что таблицы DB во всех случаях определены как latin1: несовместимы только наборы символов по умолчанию для серверов.

1 Ответ

2 голосов
/ 14 июня 2011

Мне кажется, это ошибка: почему кодировка соединения должна иметь какое-либо влияние на данные для двоичного столбца, особенно если он идентифицирован как двоичный для самого PDO с помощью PARAM_LOB?

Я не думаю, что это должно быть ошибкой. Я могу себе представить, что всякий раз, когда клиент общается с сервером и говорит, что следующая команда находится в UTF-8, а сервер нуждается в ней в Latin-1, запрос может быть перекодирован до предварительного анализа и выполнения. Так что это проблема кодирования для передачи данных. Так как повторное кодирование будет влиять на весь предварительный анализ всего запроса, также изменятся двоичные данные для столбца BLOB.

Из руководства Mysql:

В какой набор символов сервер должен переводить инструкцию после ее получения?

Для этого сервер использует системные переменные character_set_connection и collation_connection. Он преобразует операторы, отправленные клиентом, из character_set_client в character_set_connection (за исключением строковых литералов, у которых есть средство представления, например _latin1 или _utf8). collation_connection важна для сравнения литеральных строк. Для сравнения строк со значениями столбцов collation_connection не имеет значения, поскольку столбцы имеют свои собственные параметры сортировки, которые имеют более высокий приоритет сортировки.

Или на обратном пути: данные Latin1 из хранилища будут преобразованы в UTF-8, потому что клиент сказал серверу, что предпочитает UTF-8 для транспортировки.

Идентификатор самого PDO, который вы называете, выглядит как нечто совершенно другое:

PDO :: PARAM_LOB указывает PDO отобразить данные как поток, чтобы вы могли манипулировать ими с помощью PHP Streams API. ( Ref )

Я не эксперт по MySQL, но я бы объяснил это так. Клиент и сервер должны договориться о том, какие кодировки они используют, и я полагаю, что они делают это по причине.

...