duan0514324 2015-04-20 16:36
浏览 70
已采纳

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

I'm using PDO to retrieve a 5 MB value in a MEDIUMBLOB column of a table in a MySQL database. A MEDIUMBLOB can store up to 16 MB, but PDO cuts it off at 1 MB because of max_allowed_packet. I tried bindColumn as mentioned in Large Objects, but PDO's MySQL driver produces a string, not a stream (bug 40913, reported as "still present in PHP-5.6.5"). In effect, it treats PDO::PARAM_LOB as a synonym for PDO::PARAM_STR. Answers to BLOB Download Truncated at 1 MB... recommend increasing max_allowed_packet variable in my.cnf on the server, but I lack permission to make changes to my.cnf, which could affect other users of the server. I know it's possible to work around this because phpMyAdmin can download such a large BLOB from the same server.

The table is defined thus:

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;

The PHP code:

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

So how should a program retrieve a large BLOB? Or would it be more practical to store each value in a file in a directory and then have a periodic task that removes any file from the directory that does not correspond to an unexpired entry in cache_items?

  • 写回答

1条回答 默认 最新

  • dony39517 2015-04-20 16:36
    关注

    I worked around this by making a loop that uses MySQL's SUBSTRING function to read smaller chunks of the value in a loop, where each chunk is smaller than a packet.

    <?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);
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥30 关于<main>标签页面跳转的问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加
  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测