2017-05-01 4 views
-1

Der folgende Code ist der Originalcode eines Perl CGI-Skripts, das wir verwenden. Selbst für sehr große Dateien scheint es zu funktionieren, aber nicht für wirklich große Dateien.Nicht genügend Arbeitsspeicher für eine sehr große Binärdatei über HTTP

Der aktuelle Code ist:

$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; 

Wenn ich den Code richtig verstehen, ist es die gesamte Datei in den Speicher geladen wird, bevor „Drucken“ es. Ich nehme an, es wäre viel besser, es per Chunk zu laden und anzuzeigen. Aber nachdem ich viele Foren und Tutorials gelesen habe, bin ich immer noch nicht sicher, wie es am besten geht, mit Standard-Perl-Bibliotheken ...

Letzte Frage, warum wird "binmode" am Ende angegeben?

Vielen Dank für jeden Hinweis oder Rat,

+0

Diese Frage eng mit [Warum ist mein Bild herunterladen CGI-Skript geschrieben in Perl nicht? ] (https://stackoverflow.com/q/10563275/100754), in dem der Code in beiden anscheinend kopiert worden ist von der gleichen beschissenen Tutorialseite. Siehe auch meinen Blog-Beitrag [Ein CGI-Skript zum Herunterladen von Dateien in Perl] (https://www.nu42.com/2012/05/file-download-cgi-script-in-perl.html), das ich als Antwort darauf geschrieben habe Frage. Die beiden Fragen sind keine genauen Duplikate, weil sie aus verschiedenen Gründen versagen. –

+0

** "Warum wird" binmode "am Ende angegeben?" ** ... Weil jemand, der nicht wusste, was er tat, Code von einem Tutorial kopierte, das von jemand anderem geschrieben wurde, der auch nicht wusste, was er tat. Da das eigentliche Datei-Handle 'DLFILE' ist, wird' binmode'ing '$ DLFILE' tatsächlich sowieso nichts tun, egal wo es platziert ist. Sein einziger Zweck im Leben besteht darin, anzuzeigen, dass die inkompetente Person, die das Drehbuch schrieb, nicht "streng" verwendete. –

+0

Dieses * dumme * 14 Jahre alte Skript: 'https: // www.sitepoint.com/datei-download-script-perl /' –

Antwort

5

Ich habe keine Ahnung, was binmode $DLFILE ist. $DLFILE hat nichts mit dem Dateihandle DLFILE zu tun, und es ist ein bisschen spät, den binmode der Datei jetzt zu setzen, dass es bis zum Ende gelesen wurde. Es ist wahrscheinlich nur ein Fehler

Sie können dies stattdessen verwenden. Es nutzt modernes Best Practices Perl und liest und sendet die Datei in 8K chunks

Der Dateiname von $ID gemacht zu werden scheint, so bin ich nicht sicher, dass $name richtig sein würde, aber ich kann nicht

sagen Achten Sie darauf, die Klammern zu halten, da der Block macht Perl den alten Wert von $/ wiederherstellen und schließen Sie die geöffnete Datei handhaben ist

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>; 
} 
+3

Während die Antwort wahr ist, wird jeder, der hier nach einer solchen Lösung sucht (zB Anfänger) * nicht verstehen, was genau das 'local $/= \ (8 * 1024);' (besonders das '\ (' part). Eine kurze Erklärung wäre nett. (Nur IMHO). – jm666

+0

Lieber Borodin, vielen Dank für deine Antwort. Ich habe es ein wenig anders gemacht, aber ich denke, es ist das gleiche schließlich? Ich habe: while (lies ($ fh, my $ buf, 64 * 1024)) {print $ buf;} –

+0

PS Eine weitere Verbesserung für jeden, der dies liest: meine $ filesize = -s $ files_location; print "Inhalt-Länge: $ filesize \ n"; –

3

Sie sind auf einmal die gesamte Datei in den Speicher zu ziehen. Es empfiehlt sich, die Datei zeilenweise zu durchlaufen, wodurch dieses Problem behoben wird.

Beachten Sie auch, dass ich den Code geändert habe, um den richtigen 3-Arg open zu verwenden, und ein lexikalisches Dateihandle anstelle eines globalen Barewords zu verwenden.

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; 
} 

Der binmode Anruf erscheint im Kontext nutzlos zu sein, was du hier gezeigt haben, wie $DLFILE kein gültiger, in gebrauchs variabel zu sein scheint (add use strict; und use warnings; an der Spitze Ihrer Skript ...)

+2

1. Binärdateien haben keine Zeilen.Am besten, '$ /' auf '\ (64 * 1024)' oder so einzustellen. 2. Die Lösung für den Missbrauch von "binmode" ist es, es richtig zu verwenden, nicht zu beseitigen. Fügen Sie 'binmode ($ fh);' hinzu oder verwenden Sie '<: row.' anstelle von' <'. – ikegami

Verwandte Themen