0

我正在尝试将一个大文件从外部 API 传递给用户,(想想 100MB 或更多)

目前,我正在使用一些偏执的脚本(由于过去的失败)来尽快下载脚本。

通过“下载”,我仅指浏览器上的下载触发器,而不是文件的实际下载。只是用户可以选择他想要保存文件的位置。

set_time_limit(0);

apache_setenv('no-gzip', 1);
ini_set('zlib.output_compression', 0);
ini_set('output_buffering', 0);
ini_set('implicit_flush', 1);
for($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); }
ob_implicit_flush(1);

header('Content-Description: File Transfer');
header('Content-type: application/octet-stream');
header('Content-Transfer-Encoding: Binary');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: private');

ob_flush();
flush();

$fh = fopen($external_api_url, 'rb');
while(!feof($fh))
{
    echo fread($fh, 512);
    ob_flush();
    flush();
}
fclose($fh);

使用这个脚本,一个 50mb 的文件在下载弹出窗口出现之前仍然需要 20 秒,而更大的文件则需要更长的时间。

有什么方法可以更快地启动流?

编辑:我也尝试过 fpassthru() 和 readfile() 但是对于同一个 50mb 文件需要 40 秒,这让我觉得这种方式更好。我也玩过不同的读取大小(512、256、64,还有其他几个),但我没有注意到差异)

4

1 回答 1

0

您的浏览器需要很长时间才能触发下载对话框的原因是 API 需要很长时间才能返回其第一个数据块,例如,它们可能在开始将数据发送到之前将整个文件读取到内存中你,这可以解释为什么更长的文件需要更长的时间才能启动,即使你总是尽可能快地写入前 512 个字节。

我试图通过从本地文件中读取来在本地模拟它,但在循环sleep(5);之前有一个语句。while

通过省略该行,我能够让谷歌浏览器在任何数据之前开始下载,同时在尝试读取文件之前header('Content-type: application/octet-stream');仍然发出调用。flush();(你已经在做这第二部分了)

然而,这似乎不适用于 Firefox 3.6,因此您可能需要针对不同浏览器使用不同的噱头,除非您可以预测文件的第一个字符(查看BOM)并在任何内容之前回显,随后将其从第一次fread()通话的开始。

我希望它有帮助!但基本上外部 API 让你大吃一惊。

于 2013-05-21T18:23:44.037 回答