在PDO MySQL中读取MEDIUMBLOB时,大约需要1 MB max_allowed_pa​​cket

时间:2015-04-20 16:36:51

标签: php mysql pdo

我使用PDO检索MySQL数据库中表的MEDIUMBLOB列中的5 MB值。 MEDIUMBLOB最多可存储16 MB,但由于max_allowed_packet,PDO会将其减少为1 MB。我尝试了Large Objects中提到的bindColumn,但是PDO的MySQL驱动程序生成了一个字符串,而不是一个流(bug 40913,报告为"仍然存在于PHP-5.6中0.5&#34)。实际上,它将PDO::PARAM_LOB视为PDO::PARAM_STR的同义词。 BLOB Download Truncated at 1 MB...的答案建议在服务器max_allowed_packet上增加my.cnf变量,但我无权更改my.cnf,这可能会影响服务器的其他用户。我知道可以解决这个问题,因为phpMyAdmin可以从同一台服务器下载这么大的BLOB

该表定义如下:

CREATE TABLE IF NOT EXISTS cache_items (
  `cache_id` INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  `name` VARBINARY(63),
  `value` MEDIUMBLOB NOT NULL,
  `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  `expires` DATETIME NOT NULL,
  UNIQUE (`name`),
  INDEX (`expires`)
) ENGINE=INNODB;

PHP代码:

<?php
require_once("dbsettings.php");
$db = new PDO($pdo_dsn, $pdo_username, $pdo_password, $pdo_options);
$name = 'hello';
$read_stmt = $db->prepare("
SELECT `value` FROM `cache_items`
WHERE `name` = :n AND `expires` > CURRENT_TIMESTAMP
ORDER BY `cache_id` DESC LIMIT 1
");
$read_stmt->execute([':n' => $name]);
$read_stmt->bindColumn(1, $value_fp, PDO::PARAM_LOB);
$ok = $read_stmt->fetch(PDO::FETCH_BOUND);
echo gettype($bodyfp);  // string, not resource, because of bug 40913
echo strlen($bodyfp);   // 1048576, not 5xxxxxx, because of max_allowed_packet

那么程序应该如何检索大BLOB?或者将每个value存储在目录中的文件中是否更实际,然后定期执行任务,从目录中删除与cache_items中未过期的条目不对应的任何文件?

1 个答案:

答案 0 :(得分:0)

我通过创建一个循环使用MySQL的SUBSTRING函数来读取循环中值的较小块来解决这个问题,其中每个块都小于一个数据包。

<?php
// [connection setup omitted]
$stat_stmt = $db->prepare("
SELECT `cache_id`, LENGTH(`value`) FROM `cache_items`
WHERE `name` = :n AND `expires` > CURRENT_TIMESTAMP
ORDER BY `cache_id` DESC LIMIT 1
");
$read_stmt = $db->prepare("
SELECT SUBSTRING(`value` FROM :start + 1 FOR 250000)
FROM `cache_items`
WHERE `cache_id` = :id
");

// Find the ID and length of the cache entry
$stat_stmt->execute($stat_stmt, [':n'=>$name]);
$files = $stat_stmt->fetchAll(PDO::FETCH_NUM);
if (!$files) {
    exit;
}
list($cache_id, $length) = $files[0];

// Read in a loop to work around servers with small MySQL max_allowed_packet
// as well as the fact that PDO::PARAM_LOB on MySQL produces a string instead
// of a stream
// https://bugs.php.net/bug.php?id=40913
$length_so_far = 0;
$body = [];
while ($length_so_far < $length) {
    $read_stmt->execute([':id'=>$cache_id, ':start'=>$length_so_far]);
    $piece = $read_stmt->fetchAll(PDO::FETCH_COLUMN, 0);
    if (!$piece) {
        exit;
    }
    $piece = $piece[0];
    if (strlen($piece) < 1) {
        exit;
    }
    $length_so_far += strlen($piece);
    $body[] = $piece;
}
echo implode('', $body);