Thread Fileupload mit Statusanzeige
(17 answers)
Opened by Paulaner at 2010-08-08 16:21 2010-08-08T17:45:55 Paulaner Du musst dem Konstruktor der CGI-Klasse eine Subroutinenreferenz mitgeben. Diese Subroutine wird dann aufgerufen jedesmal wenn Uploaddaten eingelesen wurden. Quote Ja, zum Beispiel. Irgendetwas muss das CGI-Skript ja ausgeben, und sei es nur ein Umleitungsbefehl. Quote Die Pufferung im eigentlichen Sinne abschalten kann man bei Perl nicht, aber man kann sie entweder umgehen indem man syswrite statt print verwendet oder man kann automatisch den Puffer nach jedem print leeren lassen, indem man zum Beispiel use IO::Handle; [...] $some_file->autoflush(1); ausführt. Quote Schneller wohl nicht, aber es wäre kein Problem, das zu machen und es könnte für das Skript, das die Statusanzeige erledigt, praktischer sein. Quote Nicht doch, das ist alles ganz einfach :-) Kleines Beispiel gefällig? Code (perl): (dl
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #!/usr/bin/env perl use 5.012; use warnings; use constant { DB_URI => 'dbi:SQLite:dbname=/tmp/uploader.db', UPLOAD_DIR => '/tmp/uploader', MAX_UPLOAD_SIZE => 1232896, # 1 MiB }; use DBI; use CGI; use CGI::Carp qw(fatalsToBrowser); use File::Temp qw(tempdir); use File::Spec::Functions qw(catfile); # Initialize database connection and prepare statements. my $db = DBI->connect(DB_URI, { RaiseError => 1, PrintError => 0}); my %statements = ( new_upload => $db->prepare(<<EOS), INSERT INTO Uploads (dir, user, received, total) VALUES (?, NULL, 0, ?); EOS upload_received => $db->prepare(<<EOS), UPDATE Uploads SET received = received + ? WHERE dir = ?; EOS upload_complete => $db->prepare(<<EOS), UPDATE Uploads SET user = ?, total = received WHERE dir = ?; EOS ); # Initialize CGI context. $CGI::POST_MAX = MAX_UPLOAD_SIZE; my $cgi = CGI->new(\&process_upload, undef, 0) or die "Failed to create CGI context"; # Processor of uploaded data. my $dropdir; my %uploads; sub process_upload { my ($name, $buffer, $nbytes) = @_; warn "$name: $nbytes"; my $target = $uploads{$name}; unless (defined($target)) { unless (defined($dropdir)) { $dropdir = tempdir('upload.XXXXXXXX', DIR => UPLOAD_DIR) or die "Failed to create drop directory: $!"; $statements{new_upload}->execute($dropdir, $ENV{CONTENT_LENGTH}); $statements{new_upload}->finish(); } my $sane = $name; $sane =~ s/^[._]+/_/g; $sane =~ s/[^-._0-9A-Za-z]/_/g; open($target, '>:raw', catfile($dropdir, $sane)) or die "Failed to open upload target for '$name': $!"; $uploads{$name} = $target; } syswrite($target, $buffer, $nbytes) or die "Write error during upload of '$name': $!"; $statements{upload_received}->execute($nbytes, $dropdir); $statements{upload_received}->finish(); } # Finish the uploads and generate a response page. # Note that we cannot determine the value of the "user" parameter earlier, # since the client may actually send it as part of the post data and after # the uploaded files! my $user = $cgi->param('user'); print $cgi->header('text/plain'); say "Uploads for ".($user // 'unknown user')." finished:"; while (my ($name, $target) = each(%uploads)) { close($target) or die "Failed to close upload target for '$name': $!"; say " * $name"; } # Write final status into the database. if (defined($dropdir)) { # In a real application the user id should be validated somehow. $statements{upload_complete}->execute($user, $dropdir); $statements{upload_complete}->finish(); } # Close database connection. $db->disconnect(); When C++ is your hammer, every problem looks like your thumb.
|