通过 HTTP 提供非常大的二进制文件时内存不足

Out of memory when serving a very big binary file over HTTP

下面的代码是我们正在使用的Perl CGI 脚本的原始代码。即使对于非常大的文件,它似乎也能正常工作,但对于非常大的文件就不行了。

当前代码是:

$files_location = $c->{target_dir}.'/'.$ID;
open(DLFILE, "<$files_location") || Error('open', 'file');
@fileholder = <DLFILE>;
close (DLFILE) || Error ('close', 'file');

print "Content-Type:application/x-download\n";
print "Content-Disposition:attachment;filename=$name\n\n";
print @fileholder;
binmode $DLFILE;

如果我对代码的理解正确,它会在 "printing" 之前将整个文件加载到内存中。当然,我想按块加载和显示它会好得多吗?但是在阅读了许多论坛和教程之后,我仍然不确定如何使用标准的 Perl 库来做到最好...

最后一个问题,为什么最后指定"binmode"?

非常感谢任何提示或建议,

您一次将整个文件拉入内存。最好逐行遍历文件,这样就消除了这个问题。

另请注意,我修改了代码以使用正确的 3-arg open,并使用词法文件句柄而不是全局裸词句柄。

open my $fh, '<', $files_location or die $!;

print "Content-Type:application/x-download\n";
print "Content-Disposition:attachment;filename=$name\n\n";

while (my $line = <$fh>){
    print $line;
}

binmode 调用在您此处显示的上下文中似乎没有用,因为 $DLFILE 似乎不是有效的正在使用的变量(添加 [=脚本顶部的 13=] 和 use warnings;...)

我不知道 binmode $DLFILE 是干什么用的。 $DLFILE 与文件句柄DLFILE 无关,现在已经读到最后了,设置文件的binmode 有点晚了。这可能只是一个错误

您可以改用它。它使用现代 Perl 最佳实践并以 8K 块读取和发送文件

文件名似乎是由$ID组成的,所以我不确定$name是否正确,但我不知道

确保保留大括号,因为该块使 Perl 恢复 $/ 的旧值并关闭打开的文件句柄

my $files_location = "$c->{target_dir}/$ID";

{
    print "Content-Type: application/x-download\n";
    print "Content-Disposition: attachment; filename=$name\n\n";

    open my $fh, '<:raw', $files_location or Error('open', "file $files_location");
    local $/ = \( 8 * 1024 );

    print while <$fh>;
}