一尘不染

PHP / PDO / MySQL:插入MEDIUMBLOB会存储错误数据

mysql

我有一个简单的PHP Web应用程序,该应用程序通过文件上传接受图标图像并将其存储在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;在其他人身上是拉丁的。

通过添加PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES latin1 COLLATE latin1_general_ci"到PDO构造函数可以“解决”问题。

在我看来,这似乎是 个错误的 设计:为什么 连接
的字符集对二进制列的数据有任何影响,特别是当使用PARAM_LOB将其识别为PDO本身的二进制数据时?

请注意,在所有情况下,数据库表都定义为latin1:只有服务器的默认字符集不一致。


阅读 336

收藏
2020-05-17

共1个答案

一尘不染

在我看来,这似乎是个错误:为什么连接的字符集对二进制列的数据有任何影响,特别是当使用PARAM_LOB将其标识为PDO本身为二进制时?

我认为这一定不是错误。我可以想象,每当客户端与服务器对话并说以下命令位于UTF-8中,而服务器需要使用Latin-1时,则查询可能会在解析和执行之前得到重新编码。因此,这是数据传输的编码问题。由于整个查询之前的解析将受到此重新编码的影响,因此BLOB列的二进制数据也将发生变化。

从Mysql手册中:

服务器在收到语句后应将其转换为什么字符集?

为此,服务器使用character_set_connection和collat​​ion_connection系统变量。它将客户端发送的语句从character_set_client转换为character_set_connection(具有诸如_latin1或_utf8之类的介绍符的字符串文字除外)。collat​​ion_connection对于比较文字字符串很重要。对于将字符串与列值进行比较,collat​​ion_connection无关紧要,因为列具有自己的排序规则,排序规则优先级更高。

或在返回途中:来自商店的Latin1数据将转换为UTF-8,因为客户端告诉服务器它更喜欢使用UTF-8进行运输。

您命名的PDO本身的标识符看起来完全不同:

PDO :: PARAM_LOB 告诉PDO将数据映射为流,以便您可以使用PHP Streams
API对其进行操作。(参考

我不是MySQL专家,但是我会这样解释。客户端和服务器需要协商使用的是哪些字符集,我认为他们这样做是有原因的。

2020-05-17