如何使用Perl提供一个供下载的大文件?
我需要从web服务器无法访问的位置下载一个大文件(500+MB)。我发现了这个问题,这与我的情况相同,但我使用的是Perl而不是PHP 我试图简单地逐行打印文件,但这不会导致浏览器在获取整个文件之前提示下载:如何使用Perl提供一个供下载的大文件?,perl,file-io,download,Perl,File Io,Download,我需要从web服务器无法访问的位置下载一个大文件(500+MB)。我发现了这个问题,这与我的情况相同,但我使用的是Perl而不是PHP 我试图简单地逐行打印文件,但这不会导致浏览器在获取整个文件之前提示下载: use Tie::File; open my $fh, '<', '/path/to/file.txt'; tie my @file, 'Tie::File', $fh or die 'Could not open file: $!'; my $size_in_bytes
use Tie::File;
open my $fh, '<', '/path/to/file.txt';
tie my @file, 'Tie::File', $fh
or die 'Could not open file: $!';
my $size_in_bytes = -s $fh;
print "Content-type: text/plain\n";
print "Content-Length: $size_in_bytes\n";
print "Content-Disposition: attachment; filename=file.txt\n\n";
for my $line (@file) {
print $line;
}
untie @file;
close $fh;
exit;
使用Tie::File;
打开我的$fh,readline函数称为readline
(也可以写成
)
我不确定你有什么问题。也许循环就是这样
没有懒散地评估(事实并非如此)。或者,也许Tie::File是
把事情搞砸了?无论如何,用于读取文件的惯用Perl
一次一行是:
open my $fh, '<', $filename or die ...;
while(my $line = <$fh>){
# process $line
}
打开我的$fh,回答(原始)问题(“Perl是否具有与PHP的readline()
函数等效的功能…?”),答案是“尖括号语法”:
(正如我最初写的)将文件的内容复制到一个列表中,并对其进行迭代。使用
while (my $line = <$filehandle>) { ... }
while(my$line=){…}
没有。在处理小文件时,差异并不显著,但在处理大文件时,差异肯定是显著的
回答(更新的)问题(“Perl是否具有与PHP的readfile()
function…?”)等价的功能?”答案是。有一个,但是Perl6::Slurp
似乎是当前选择的模块
隐含的问题(“为什么浏览器在抓取整个文件之前不提示下载?”)与您在文件中的阅读方式完全无关,而与浏览器认为良好的形式有关。我猜浏览器会看到mime类型并决定它知道如何显示纯文本
更仔细地看一下内容配置问题,我记得IE忽略内容配置也有类似的问题。不幸的是,我记不起解决办法了。(旧页,参考IE 5.0、5.5和6.0)。然而,为了澄清,我想知道:
您使用什么样的链接指向这个大文件(即,您使用的是普通的a href=“perl\u script.cgi?filename.txt
链接还是某种Javascript)
您实际使用什么系统来提供文件?例如,Web服务器是否自己连接到另一台没有Web服务器的计算机,然后将文件复制到Web服务器,然后将文件发送给最终用户,或者用户是否直接连接到没有Web服务器的计算机
在最初的问题中,您写道“这不会导致浏览器在获取整个文件之前提示下载”,在评论中,您写道“在下载整个文件之前,我仍然无法获得文件的下载提示”。这是否意味着该文件会显示在浏览器中(因为它只是文本),在浏览器下载整个文件后,您会收到“您想将此文件保存在何处”提示或其他提示
我有一种感觉,HTTP头有可能在某个点被剥离,或者缓存控制头被添加(这显然会造成麻烦)。如果您只想将输入转换为输出,这应该可以做到
use Carp ();
{ #Lexical For FileHandle and $/
open my $fh, '<' , '/path/to/file.txt' or Carp::croak("File Open Failed");
local $/ = undef;
print scalar <$fh>;
close $fh or Carp::carp("File Close Failed");
}
应用jrockways建议
{
打开我的$fh,“你可以使用我的模块。它应该是高效的(因为它在引擎盖下使用sendfile),但不是完全可移植的(目前只支持Linux、FreeBSD和Solaris)。当你说“这不会导致浏览器提示下载”——什么是“浏览器”
不同的浏览器表现不同,IE特别故意,它会忽略标题,并根据读取文件的前几kb自行决定做什么
换句话说,我认为您的问题可能在客户端,而不是服务器端
试着对“浏览器”撒谎,告诉它文件的类型是application/octet stream。或者为什么不直接压缩文件,特别是因为它太大了。不要使用for/foreach()
,因为它一次读取整个文件,然后在上面迭代。使用while()
取而代之。sysread
解决方案很好,但是sendfile
性能最好。我成功地告诉浏览器它是application/octet-stream类型,而不是text/plain类型。显然,大多数浏览器更喜欢以内联方式显示text/plain,而不是给用户一个下载对话框选择权
从技术上讲,这是在欺骗浏览器,但它确实起到了作用。为大型文件提供下载服务的最有效方式取决于您使用的web服务器
除了:
有一些链接描述如何为(mod_secdownload:security via url generation)执行此操作。有一些PHP、Ruby(Rails)、Python中的示例可用于Perl
基本上可以归结为:
配置web服务器的路径和权限
在Perl应用程序中为重定向生成有效的头(内容类型
,内容处置
,内容长度
?,X-Sendfile
或X-Accel-redirect
等)
可能有一些CPAN模块、web框架插件可以做到这一点,例如,+1正确回答隐含的问题(至少是我想说的方式)对不起,我想说的是readfile(),正如我链接的问题中所建议的。问题已更新。比较for()
对不起,我想说的是readfile(),正如我链接的问题中所建议的。问题已更新。我尝试先使用一个不带Tie::Cache的while循环,其行为与Tie::Cache相同。在下载整个文件之前,我仍然没有收到文件的下载提示。那么,最好让您的web框架来处理它。试试HTTP::Engine,它与CGI一起工作。我认为我需要完全
for my $line (<$filehandle>) { ... }
while (my $line = <$filehandle>) { ... }
use Carp ();
{ #Lexical For FileHandle and $/
open my $fh, '<' , '/path/to/file.txt' or Carp::croak("File Open Failed");
local $/ = undef;
print scalar <$fh>;
close $fh or Carp::carp("File Close Failed");
}
{
open my $fh , '<', '/dev/sda' ;
local $/ = \8192; # this tells IO to use 8192 char chunks.
print $_ while defined ( $_ = scalar <$fh> );
close $fh;
}
{
open my $fh , '<', '/dev/sda5' ;
print $_ while ( sysread $fh, $_ , 8192 );
close $fh;
}