www.farid-hajji.net banner

Farid Hajji

Perl: Einführung, Anwendungen, Referenz (2/e) [Support-Site]

Farid Hajji: Perl - Einführung, Anwendungen, Referenz
2., aktualisierte und erweiterte Auflage
Addison-Wesley Longman, ISBN 3-8273-1535-2

File-Upload Techniken

Motivation

Oft stellt sich die Frage, wie User Dateien zu einem Webserver übertragen können. Bei einer Anwendung geht es z.B. darum, Winword-Dokumente der Allgemeinheit oder einem eingeschränkten Kreis zur Verfügung zu stellen. Eine andere Anwendung sieht z.B. vor, GIF- oder JPEG-Dateien in eine Kontaktdatenbank zu speichern etc.

In all diesen Fällen soll eine Datei auf dem lokalen Rechner des Anwenders über ihren Namen ausgewählt und anschließend zum Webserver übertragen werden. Dies ist sozusagen der umgekehrte Weg, den man beim Surfen sonst geht.

Es gibt diverse File-Upload-Techniken:



In diesem Abschnitt werden wir diese Techniken anhand einfacher Beispielprogramme erläutern.

File-Upload in einem Formular mit CGI.pm

Mit Hilfe des File-Upload-Features von Netscape kann innerhalb eines Formulars ein Dateiauswahl-Button bzw. Eingabewidget eingeblendet werden. Damit kann der User eine Datei auf seinem lokalen Rechner auswählen, die er zum Webserver übertragen möchte. Sobald dann das Formular mittels submit abgeschickt werden soll, wird der Inhalt dieser selektierten Datei zusammen mit den restlichen Daten des Formulars zum Webserver gesendet.

Der Webserver ruft in diesem Fall wie gewohnt ein CGI-Programm auf, und übergibt ihm die empfangenen Daten, also auch den Inhalt der so übermittelten Datei. Die Aufrufmethode wird sicherlich POST sein. Es liegt nun in der Verantwortung des CGI-Programms, die empfangenen Formulardaten auszulesen und im Falle des Dateiinhaltes, etwas damit zu tun (z.B. diesen Inhalt serverseitig irgendwo abzuspeichern).

Das CGI-Programm cgi-upload.pl verwendet hierfür das CGI.pm-Modul. Um dieses Programm auch zu verstehen, benötigen Sie noch folgende Informationen:

Die PUT-Methode und ihre Verwendung

Wir haben bisher die HTTP-Methode POST benutzt, um den Inhalt einer auf dem Rechner des Webclients liegenden Datei zum Webserver zu übertragen. Ein viel natürlicherer Weg ist der Einsatz der HTTP-Methode PUT.

Wie sieht der Dialog zwischen einem Webclient und einem Webserver aus, wenn der Client eine Datei mittels PUT senden möchte? Der Webclient kontaktiert wie gewohnt einen Webserver und sendet ihm eine Anforderung. Diese Anforderung beginnt jedoch nicht wie bisher mit dem Aufruf der GET- oder POST-Methode, sondern mit einem PUT-Aufruf. Am Anschluß an das Schlüsselwort PUT folgt die gewünschte Ziel-URL auf dem Webserver, sowie der Version des HTTP-Protokolls. Auf den nächsten Zeilen folgen weitere Informationen, wie etwa der virtuelle Host, der Name des sendenden Programms sowie die Länge der zu uploadenden Datei. Nach einer leeren Zeile folgt dann der Inhalt dieser zu übertragenden Datei. Eine Anforderung eines Webclients, der die PUT-Methode verwenden möchte, sieht also wie folgt aus:

PUT /putdest/atest.xxx HTTP/1.0
Host: sun-1.meta.net:8812
User-Agent: libwww-perl/5.41
Content-Length: 32

this is a test
with many lines.

In diesem Beispiel hat ein Programm versucht, mit Hilfe der PUT-Methode eine aus zwei Zeilen und 32 Zeichen bestehenden Datei zum Webhost sun-1.meta.net:8812 zu senden. Die Datei sollte nach Vorstellungen des Webclients unter der URL /putdest/atest.xxx beim Webhost abgelegt werden, was zu einer URL von http://sun1.meta.net:8812/putdest/atest.xxx führen müßte.

An dieser Stelle sollte der angesprochene Webserver etwas mit den gesendeten Daten und URL tun und wie gewohnt mit einem HTTP-Antwortcode reagieren.

Schauen wir uns erst einmal den Code eines Webclients an, der diesen PUT-Aufruf erzeugt. Das Programm put-test.pl ist zunächst eine ganz normale Anwendung der LWP::*-Library und somit ein gewöhnlicher Webclient. Aus diesem Grunde wird auch ein LWP::UserAgent-Objekt erzeugt. Was jedoch hier neu ist, ist die Verwendung eines HTTP::Request::Common-Objekts zur Erzeugung eines PUT-Requests. Zusammen mit dem Request wird der Inhalt der eingelesenen lokalen Datei mitübertragen. Der entscheidende Code lautet:

# Wir erzeugen eine HTTP PUT-Anforderung und senden als Inhalt
# die in $content eingelesene Datei zum Webserver:
$ua  = new LWP::UserAgent;
$res = $ua->request(PUT $url,
                    'Content' => $content);

Der Rückgabecode wird wie gewohnt abgefragt.

Testen wir dieses kleine Programm mal an einem Dummy-Server, den wir selbst manuell installieren. Dazu verwenden wir das Tool nc, und werden manuell auf die PUT-Anforderung reagieren:

farid@sun-1:~> nc -l -p 8812
PUT /a/path/file.txt HTTP/1.0
Host: localhost:8812
User-Agent: libwww-perl/5.41
Content-Length: 305

root::0:root
other::1:
bin::2:root,bin,daemon
sys::3:root,bin,sys,adm
adm::4:root,adm,daemon
uucp::5:root,uucp
mail::6:root
tty::7:root,tty,adm
lp::8:root,lp,adm
nuucp::9:root,nuucp
staff::10:
daemon::12:root,daemon
sysadmin::14:
nobody::60001:
noaccess::60002:
nogroup::65534:
users::101:
www::102:farid
HTTP/1.0 200 OK

^C punt!
farid@sun-1:~> 

Hier wurden die Benutzereingaben fett angezeigt. Nach dem Aufruf des nc-Kommandos passiert erst einmal gar nichts. In einem anderen Fenster rufen wir nun put-test.pl wie folgt auf:

farid@sun-1:~> put-test.pl http://localhost:8812/a/path/file.txt /etc/group
OK: farid@sun-1:~>

Was ist hier geschehen? Das put-test.pl-Programm hat eine Verbindung mit dem auf Port 8812 wartenden nc-Programm aufgebaut (siehe Kapitel 17) und anschließ die PUT-Anforderung samt Inhalt der Datei /etc/group zum nc-Server gesendet.

Nach dem Senden der Datei wartet nun put-test.pl auf eine Antwort des Servers. Diese senden wir ihm selbst, indem wir manuell im nc-Fenster den Code HTTP/1.0 200 OK getippt haben, gefolgt von einer leeren Zeile. Anschließend beenden wir (d.h. der Server nc) die Verbindung, hier speziell indem wir CTRL-C drücken und somit sowohl die Verbindung beenden als auch den nc-Server abbrechen.

Genau das geschieht, wenn Sie vom Netscape-Composer aus, eine Datei mittels Publish zu einem Webserver senden. Probieren wir es doch einfach mal aus. Wir erzeugen eine kleine Datei im Netscape-Composer und senden diese dann zum neugestarteten nc-Server.

So sieht die Eingabemaske des Netscape-Composer aus, wenn wir auf den Publish-Button geklickt haben:

File-Upload mit Netscape Composer

Dieses Fenster erscheint, während der Übertragung der Datei zum nc-Server:

File-Upload Bestätigung

Beim nc-Server sieht es hingegen so aus:

farid@sun-1:~> nc -l -p 8812
PUT /a/path/atest.html HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.5 [en] (X11; I; SunOS 5.6 i86pc)
Pragma: no-cache
Host: localhost:8812
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Content-Length: 399

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
   <meta name="Author" content="Farid Hajji">
   <meta name="GENERATOR" content="Mozilla/4.5 [en] (X11; I; SunOS 5.6 i86pc) [Netscape]">
   <title>atest</title>
</head>
<body>
this is a simple test
<br>with two lines
</body>
</html>
HTTP/1.0 200 OK

farid@sun-1:~> 

Wieder wurden unsere Eingaben fett hervorgehoben.

Beachten Sie diesmal den Inhalt der HTTP-Felder. Im ersten Beispiel war unser Client die LWP::*-Library, im zweiten Beispiel hingegen der Netscape (a.k.a. Mozilla):

User-Agent: libwww-perl/5.41
User-Agent: Mozilla/4.5 [en] (X11; I; SunOS 5.6 i86pc)

Beachten Sie auch die PUT-URLs!

All dies ist schön und gut, aber wir haben bisher als Server nur den nc benutzt und zu allem Überfluß die HTTP-Rückgabecodes manuell zurückgesendet. Wie wäre es mit einem echten Webserver?

Im folgenden werden wir den Apache-Webserver verwenden, um die PUT-Anforderungen entgegenzunehmen und zu verarbeiten.

Die PUT-Methode im einem mod_perl-Handler

Eine naheliegende Methode, den Apache-Webserver zu konfigurieren, damit er die PUT-Methode verwendet, ist, einen Bereich des URL-Baumes dafür zu reservieren und für jede Anforderung innerhalb dieses Bereiches einen mod_perl-Handler verantwortlich zu erklären.

Wenn Sie Ihren Apache-Webserver, wie im Buch empfohlen, zusammen mit mod_perl kompiliert haben, können Sie nun folgende Zeilen zu Ihrer Konfigurationsdatei ~www/conf/httpd.conf (bzw. der im Buch empfohlenen eigenen Datei ~www/conf/perl.conf) hinzufügen:

<Location /putperl>
  SetHandler     perl-script
  PerlHandler    Apache::PutHandler
  <Limit GET HEAD POST PUT>
    order deny,allow
    deny from all
    allow from .meta.net
  </Limit>
</Location>

Natürlich sollten Sie .meta.net durch den Namen Ihrer (echten oder fiktiven) Domain ersetzen. Bevor Sie den Webserver neustarten, sollten Sie das folgende Programm PutHandler.pm nach ~www/lib/perl/Apache/PutHandler.pm kopieren und für den http-User (nobody) lesbar machen. Nun starten Sie Ihren Webserver durch mit /etc/init.d/apachectl restart.

Versuchen wir es nun einfach. Zunächst probieren wir es mit dem selbstgeschriebenen put-test.pl Webclient:

farid@sun-1:~> put-test.pl http://sun-1.meta.net/putperl/a/long/path/firstfile.txt /etc/group
OK: 

farid@sun-1:~> ls -l /tmp/first*
-rw-r--r--   1 nobody   nobody       305 Oct 22 01:24 /tmp/firstfile.txt

farid@sun-1:~> head -3 /tmp/firstfile.txt
root::0:root
other::1:
bin::2:root,bin,daemon

Das scheint ja richtig funktioniert zu haben. Probieren wir es nun mit dem Netscape Composer. Auch hier funktioniert es problemlos.

Wie sieht es nun mit der GET-Methode aus?

farid@sun-1:~> nc sun-1.meta.net 80
GET /putperl/firstfile.txt HTTP/1.0

HTTP/1.1 200 OK
Date: Thu, 21 Oct 1999 23:31:02 GMT
Server: Apache/1.3.6 (Unix) mod_perl/1.21
Connection: close
Content-Type: text/plain

root::0:root
other::1:
bin::2:root,bin,daemon
sys::3:root,bin,sys,adm
adm::4:root,adm,daemon
uucp::5:root,uucp
mail::6:root
tty::7:root,tty,adm
lp::8:root,lp,adm
nuucp::9:root,nuucp
staff::10:
daemon::12:root,daemon
sysadmin::14:
nobody::60001:
noaccess::60002:
nogroup::65534:
users::101:
www::102:farid
farid@sun-1:~> 

Auch mit Hilfe eines beliebigen Browsers, kann die Seite unter der URL /putperl/firstfile.txt angefordert werden:

/putperl und GET

Interessant ist hier, daß die URL /putperl und alles, was darunter liegt, nicht innerhalb der DocumentRoot liegt, sondern virtuell mit Hilfe des mod_perl Handlers PutHandler.pm erzeugt bzw. verarbeitet wird. In unserem Beispiel sind die Dateien physikalisch unter /tmp abgelegt. Sie werden im Falle des File-Uploades mittels PUT vom PutHandler.pm dorthin abgelegt und während eines GET vom selben PutHandler.pm aus /tmp ausgelesen und weitergesendet, als kämen sie von der URL /putperl. Das ist der Sinn der <Location /putperl>-Direktive in der Konfigurationsdatei.

Interessant ist hier natürlich, daß anstatt die Datei physikalisch unter /tmp zu speichern, diese auch einer DBI-Datenbank oder einem beliebigen anderen Programm zur Analyse und Speicherung übergeben werden könnte! Sowohl beim Upload, als auch beim Download würde dann die DBI-Datenbank bzw. dieses andere Programm konsultiert und würde die Datei jeweils wieder rekonstruieren. Versuchen Sie es!

Beachten Sie die Denial-of-Service Attacken! Was oben dazu gesagt wurde, gilt natürlich in einem noch größeren Umfang hier bei mod_perl! Sie sollten auch hier möglicherweise eine eingeschränkte Policy fahren und nur zugelassenen Usern den Zugriff auf die PUT-Methode erlauben und auch dann nur mit Quotas!

Warum wurde eigentlich nicht der Apache-Webserver selbst mit der Beantwortung der PUT-Methode beauftragt? Das wäre im Prinzip möglich gewesen. Leider hatte der Apache-Webserver bis einschließlig Version 1.3.6 (evtl. spätere Versionen ebenfalls) noch keinen builtin PUT-Handler. In diesem Fall würde die nicht abgefangene PUT-Anforderung beim Default-Handler des Apache-Webservers landen und dieser würde diese ablehnen.

Ein weiterer Grund, die PUT-Methode selbst mit Hilfe des mod_perl-Moduls und einem Handler wie etwa PutHandler.pm zu realisieren, ist die oben erwähnte Flexibilität bei

Die PUT-Methode mit einem CGI-Handler

In Vorbereitung.

Die FTP-Methode mit der LWP-Library

In Vorbereitung.

Die FTP-Methode mit einem Java-Applet

In Vorbereitung.

Credits

Thanks to Barry Grotjahn für die Frage nach File-Upload Techniken, die zu dieser Seite führte.

[Prev] [Up] [Next]

[Alte Quelle]


Last modified: $Date: 2006/05/18 12:55:47 $
FH. Search :: Sitemap :: Disclaimer :: Copyright :: Privacy
FreeBSD Logo