Thread Fileupload mit Statusanzeige (17 answers)
Opened by Paulaner at 2010-08-08 16:21

murphy
 2010-08-08 21:36
#140480 #140480
User since
2004-07-19
1776 Artikel
HausmeisterIn
[Homepage]
user image
2010-08-08T17:45:55 Paulaner
[...]
Objekt zu CGI erzeugen ist mir noch klar, aber nicht so ganz, was du mit dem Hook als Konstruktorparameter meinst.

Du musst dem Konstruktor der CGI-Klasse eine Subroutinenreferenz mitgeben. Diese Subroutine wird dann aufgerufen jedesmal wenn Uploaddaten eingelesen wurden.

Quote
[...]
Was meinst du mit eine Antwort für den Client erzeugen? Mit Print?

Ja, zum Beispiel. Irgendetwas muss das CGI-Skript ja ausgeben, und sei es nur ein Umleitungsbefehl.

Quote
[...]
Wie kann ich beiflussen ob die Daten gepuffert werden oder nicht?

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
[...]
Zum letzten Punkt. Im Moment muss ich nur pro "Status holen" einmal auf die Datenbank zugreifen und die gesamte Größe zu holen. Wenn ich die tatsächlichen Fortschritt auch noch speichern wollte, müsste dies ja im PERL Script während des Uploads passieren. Wäre das schneller?

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
[...]
Oh man... da hab ich mir was zugemutet. Wer hätte gedacht, dass das so kompliziert ist.

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.

View full thread Fileupload mit Statusanzeige