首页 > 解决方案 > 将非常大的 Blob 从 MySQL 流式传输到 PHP 并创建一个文件

问题描述

我们有一个 MySQL 数据库,其中有一些非常大的文件存储在 blob 字段中,例如一些超过 700MB 的视频。文件大小范围从 0.5 MB PDF 到 JPEGS 等...

我正在尝试使用 PHP 来检索这些列并在服务器上创建一个文件,该文件稍后将作为下载提供。

我目前正在使用以下方法:

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
    PDO::MYSQL_ATTR_MAX_BUFFER_SIZE => 1024*1024*500
];
try {
     $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
     throw new \PDOException($e->getMessage(), (int)$e->getCode());
}


$stmt = $pdo->prepare('SELECT a.TITLE, a.ATTVERSION, a.ATTACHMENTID, a.CONTENTTYPE, a.FILESIZE, d.DATA FROM ATTACHMENTS a
                LEFT JOIN ATTACHMENTDATA d ON d.ATTACHMENTID=a.ATTACHMENTID
                WHERE a.ATTACHMENTID= ?');
$stmt->execute([$fileid]);
$file = $stmt->fetch();

file_put_contents($storage_dir . "/" . $filename, $file['DATA']);

这适用于较小的文件(注意我将缓冲区大小设置为 500MB),但较大的文件会被截断和损坏。

接下来我尝试了 LOB 和无缓冲查询方法:

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
    $options = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES   => false,
        PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
    ];
    try {
        $pdo = new PDO($dsn, $user, $pass, $options);
    } catch (\PDOException $e) {
        throw new \PDOException($e->getMessage(), (int)$e->getCode());
    }


$stmt = $pdo->prepare('SELECT a.TITLE, a.ATTVERSION, a.ATTACHMENTID, a.CONTENTTYPE, a.FILESIZE, d.DATA FROM ATTACHMENTS a
                LEFT JOIN ATTACHMENTDATA d ON d.ATTACHMENTID=a.ATTACHMENTID
                WHERE a.ATTACHMENTID= ?');

$stmt->execute([$fileid]);
$stmt->bindColumn(1, $title, PDO::PARAM_STR, 256 );
$stmt->bindColumn(2, $attversion, PDO::PARAM_INT);
$stmt->bindColumn(3, $attid, PDO::PARAM_INT);
$stmt->bindColumn(4, $contenttype, PDO::PARAM_STR, 256);
$stmt->bindColumn(5, $filesize, PDO::PARAM_INT);
$stmt->bindColumn(6, $data, PDO::PARAM_LOB);

$stmt->fetch(PDO::FETCH_BOUND);


file_put_contents($storage_dir . "/" . $filename, $data)

使用这个选项,我只得到 1MB 的文件,它似乎忽略了 MYSQL_ATTR_USE_BUFFERED_QUERY => false 并且因为我没有设置缓冲区大小,所以它默认为 1MB。任何人都可以提供任何建议或看到任何明显的东西吗?MySQL 在不同的服务器上,但我也愿意以另一种方式这样做,并通过 PHP 调用 bash 脚本或其他东西。

标签: phpmysqlpdo

解决方案


这最初是在 php 5.4.16 上完成的,它似乎不支持无缓冲流。升级到 php 8.0.11 后,我现在可以正常工作了,除了使用 ini_set 增加 PHP 内存。


推荐阅读