我一直在尝试从mongoDB中读取一个音频文件,该文件是我使用GridFS存储的。我可以在系统中下载文件并从中播放,但我想从数据库本身流式传输这些音频/视频文件并在浏览器中播放。有没有办法在不将文件下载到系统的情况下做到这一点?任何帮助都是好的。
PHP GridFS支持有一个MongoGridFSFile::getResource()函数,它允许您将流作为资源获取,而不会在内存中加载整个文件。与fread/echo
或stream_copy_to_stream结合使用,可以防止整个文件加载到内存中。使用stream_copy_to_stream,您可以简单地将GridFSFile流的资源复制到STDOUT流:
<?php
$m = new MongoClient;
$images = $m->my_db->getGridFS('images');
$image = $images->findOne('mongo.png');
header('Content-type: image/png;');
$stream = $image->getResource();
stream_copy_to_stream( $stream, STDOUT );
?>
或者,您可以在返回的$stream
资源上使用fseek()
,只将流的一部分发送回客户端。结合HTTP范围请求,您可以非常有效地完成这项工作。
如果其他配方失败,例如NginX
和php-fpm
,因为STDOUT
在fpm
中不可用,则可以使用
fpassthru($stream);
而不是
stream_copy_to_stream( $stream, STDOUT );
因此,一个完整的解决方案看起来像:
function img($nr)
{
$mongo = new MongoClient();
$img = $mongo->ai->getGridFS('img')->findOne(array('metadata.nr'=>$nr));
if (!$img)
err("not found");
header('X-Accel-Buffering: no');
header("Content-type: ".$img->file["contentType"]);
header("Content-length: ".$img->getSize());
fpassthru($img->getResource());
exit(0);
}
FYI:
在本例中:
- 文件不是由文件名处理的,而是由存储在元数据中的数字访问的。提示:您可以设置一个唯一的索引,以确保任何数字都不能使用两次
- 内容类型也是从GridFS中读取的,所以您不需要对其进行硬编码
- 关闭NginX缓存以启用流式传输
这样你甚至可以处理其他事情,比如视频或html页面。如果您想启用NginX缓存,也许只在较大的大小上输出X-Accel-Buffering
。