ahecht: http-upload tracen / progress bar

Ich suche nach einer Möglichkeit, einen mittels http in Gang gesetzten Upload einer größeren Datei zu tracen um den Benutzer am anderen Ende über den Stand der Dinge zu informieren.

Soweit bin ich dabei gekommen: Das Formular, das den Upload in Gang setzt, taugt dazu nicht, da das zugehörige Script erst nach beendetem Upload aufgerufen wird. Also wird per onsubmit ein zweites Script aufgerufen, das nun vor der Aufgabe steht, den Namen der dem Upload zugeordneten temporären Datei herauszubekommen. Und genau hier hänge ich fest. Folgende Fragen sind aufgetaucht:

* Gibt es eine Möglichkeit, den Namen des Tempfiles (in Verbindung mit einer Session) im Voraus festzulegen, ohne am PHP Interpreter herumzubosseln?
* Gibt es u.U. eine (Umgebungs-|PHP-|Server-) Variable, aus der man auf das verwendete Tempfile schliessen kann?
* Gibt es eine Möglichkeit, die id des http-Prozesses des Uploads herauszubekommen und darüber das File zu indentifizieren?

TIA,
Andreas

  1. Hi,

    Ich suche nach einer Möglichkeit, einen mittels http in Gang gesetzten Upload einer größeren Datei zu tracen um den Benutzer am anderen Ende über den Stand der Dinge zu informieren.

    gerne: Wechsele das Protokoll. Mit HTTP ist das nicht möglich; dort werden die Daten vom Client weggejagt, und erst wenn der Server sie vollständig erhalten hat, wird Dein PHP-Script gestartet. Auf Clientseite gibt es ebenfalls keine Möglichkeit (einfach deshalb, weil sie nicht da ist).

    Übrigens hatten schon andere vor Dir die Idee, so was zu tun. Ermittle bitte über das Archiv, ob einer wenigstens den Hauch eines theoretisch möglichen Ansatzes hatte.

    Cheatah

    1. Cheatah schrieb:

      gerne: Wechsele das Protokoll. Mit HTTP ist das nicht möglich;

      Sagen wir besser: mit PHP scheint das nicht möglich zu sein. Mit ASP stellt es nämlich offenbar kein Problem dar.

      dort werden die Daten vom Client weggejagt, und erst wenn der Server sie vollständig erhalten hat, wird Dein PHP-Script gestartet.

      Das ist mir bekannt und steht in meinem Posting auch drin. Weiterhin schrieb ich auch, dass das kein Problem darstellt. Die Möglichkeit des Wechselns auf ein anderes Protokoll steht in diesem Fall nicht zur Verfügung und ich möchte sie auch nicht diskutieren.

      Übrigens hatten schon andere vor Dir die Idee, so was zu tun. Ermittle bitte über das Archiv, ob einer wenigstens den Hauch eines theoretisch möglichen Ansatzes hatte.

      Nein. Der einzige Weg, der mir gangbar erscheint, ist in meinem Posting beschrieben und mit konkreten Fragen versehen, auf die, wie es aussieht, keiner eine Antwort zu wissen scheint. :-(

      regards,
      Andreas

      1. Hoi,

        Sagen wir besser: mit PHP scheint das nicht möglich zu sein. Mit
        ASP stellt es nämlich offenbar kein Problem dar.

        Doch, tut es.

        Nein. Der einzige Weg, der mir gangbar erscheint, ist in meinem
        Posting beschrieben und mit konkreten Fragen versehen, auf die, wie
        es aussieht, keiner eine Antwort zu wissen scheint. :-(

        /?m=21793&t=3759

        Gruesse,
         CK

        1. Sagen wir besser: mit PHP scheint das nicht möglich zu sein. Mit ASP stellt es nämlich offenbar kein Problem dar.

          Doch, tut es.

          Du kannst Dich gern vom Gegenteil überzeugen, indem Du Dich z.B. bei http://www.photocase.de anmeldest und ein Bild hochlädst.

          regards,
          Andreas

  2. Hi,

    Ich suche nach einer Möglichkeit, einen mittels http in Gang gesetzten
    Upload einer größeren Datei zu tracen um den Benutzer am anderen Ende
    über den Stand der Dinge zu informieren.

    eine frommer Wunsch.
    Mit HTTP wirst Du dabei aber beträchliche Probleme haben.

    Soweit bin ich dabei gekommen: Das Formular, das den Upload in Gang
    setzt, taugt dazu nicht, da das zugehörige Script erst nach beendetem
    Upload aufgerufen wird.

    Genau. Das ist HTTP.

    Also wird per onsubmit ein zweites Script aufgerufen,

    Fein. Der Upload ist inzwischen möglicherweise fertig. Vielleicht auch
    nicht. Nur: Eine Kontrolle irgend einer Art hast Du darüber nicht.

    das nun vor der Aufgabe steht, den Namen der dem Upload zugeordneten
    temporären Datei herauszubekommen.

    Wie kommst Du darauf, daß der Upload eine temporäre Datei verwendet?
    Das _könnte_ natürlich sein. Es muß aber nicht. Bei kleinen Dateien würde
    ich es bestimmt nicht tun, wenn ich den Webserver geschrieben hätte.

    Das Ziel des "Upload" ist ja nicht, eine Datei auf dem Server zu erzeugen.
    Das Ziel ist, es auf dem Server eine Anwendung zu starten, welche den
    Inhalt eines CGI-Multipart-Pakets als Parameter erhält. Was diese Anwendung
    damit macht, ist ihre Sache - sie kann ihn genauso gut wegwerfen.

    Und genau hier hänge ich fest.

    Kein Wunder.

    * Gibt es eine Möglichkeit, den Namen des Tempfiles (in Verbindung
      mit einer Session) im Voraus festzulegen, ohne am PHP Interpreter
      herumzubosseln?

    Nein. Weder existiert eine solche Datei unbedingt, noch erlaubt Dir HTTP,
    darauf in irgend einer Weise Einfluß zu nehmen.

    * Gibt es u.U. eine (Umgebungs-|PHP-|Server-) Variable, aus der man
      auf das verwendete Tempfile schliessen kann?

    Kann ich mir nicht wirklich vorstellen

    * Gibt es eine Möglichkeit, die id des http-Prozesses des Uploads
      herauszubekommen und darüber das File zu indentifizieren?

    Du gehst immer noch davon aus, daß der Upload etwas sei, was er nicht ist.
    Passe Deine Vorstellung der Realität an.

    HTTP besteht daraus, daß der Client an den Server einen Request sendet
    und der Server eine Antwort zurück liefert.
    Einfluß auf den Ablauf des Request, insbesondere eine Möglichkeit zur
    Reaktion auf irgend einen Ablauf desselben, hast Du nicht.

    Etwas Anderes ist es, wenn Du Dein Modell in mehrere Requests zerlegen
    könntest.
    Ein Browser kann ja durchaus mehrere HTTP-Requests parallel durchführen
    - beispielsweise wenn er viele Bilder zu einem HTML-Dokument nachladen
    muß. Inwiefern das der Fall ist, darauf wiederum hast Du nur begrenzten
    Einfluß: Bei Netscape 3 und Opera kannst Du die Zahl der parallelen
    Prozesse einstellen, bei Netscape 4 und M$IE mußt Du nehmen, was deren
    Programmierer für sinnvoll halten.

    Du könntest also _theoretisch_ folgendes versuchen:

    1. Du definierst ein Frameset.

    2. Du startest den Upload, indem Du einen Submit-Event in einem der beiden
         Frames auslöst (Klick auf Button.)

    3. Die Submit-Behandlungs-Routine (JavaScript) löst _vor_ ihrer Beendigung
         einen Event in dem anderen Frame aus.

    4. Danach beendet sie sich und erlaubt dem Formular abgesendet zu werden.
         Ab hier läuft der Upload - der Event im anderen Frame ist aber schon in
         Bearbeitung!
         Der HTTP-Request wird nun irgendwann dazu führen, daß auf dem Server
         ein Programm gestartet wird, welches Deine Upload-Datei annimmt - und
         vielleicht in einer Datei abspeichert. Vielleicht auch nicht.
         Dieses Programm kannst Du selbst schreiben - dann weißt Du, was es tut.

    5. In dem anderen Frame läuft nun irgend ein JavaScript-Code von Dir.
         Dieser kann ggf. selbst eigene HTTP-Requests zum Server senden.
         Er kann insbesondere auf die Formular-Felder im anderen Frame zugreifen.
         Vielleicht kann er sogar genügend Informationen sammeln, um herauszu-
         finden, was genau dort passiert; vielleicht hätte der onSubmit-Handler
         des Upload-Frame ihm vorher auch ein paar Informationen zuspielen können.
         Mit etwas Glück könnte er also herausfinden, wie die Datei auf dem Server
         heißen soll. Mit etwas Pech reicht es nicht. JavaScript ist nicht meine
         Stärke.

    6. Mit diesem Wissen könnte der JavaScript-Code also weitere HTTP-Requests
         zu einem _anderen_ serverseitigen Skript senden.
         Wäre JavaScript eine "richtige" Programmiersprache, dann könnte es sogar
         einen HTTP-HEAD-Request auf die entstehende Datei senden - dabei würde
         es sowohl deren Existenz als auch deren Größe erfahren. (Vorausgesetzt,
         die Datei entsteht innerhalb des URL-Space des Webservers.)
         Also zurück zum Machbaren: Dieses Skript würde mit den übergebenen
         Parametern berechnen können, wie die zu erstellende Datei auf dem Server
         heißen wird. Es kann auch feststellen, ob diese Datei bereits vorhanden
         ist; es kann auch ihr letztes Änderungsdatum lesen.
         Vor allem kann dieses Skript selbst eine HTML-Seite generieren und an
         den Browser zurück senden, welcher sie in dem zweiten Frame darstellt.

    7. Diese HTML-Seite enthält nun zwei Dinge:
         a) Informationen darüber, was mit der Datei auf dem Server los ist
         b) einen META REFRESH-Header, der den Aufruf desselben (!) Skripts mit
            denselben Parametern wenige Sekunden später erneut auslöst.
            (Ersatzweise <body onload>-JavaScript-Code mit dieser Funktionalität.)
         Der zweite Frame "pollt" also den Server, um Informationen über den Stand
         der Verarbeitung des ersten Frames zu holen. Sollte der Upload signifi-
         kant lange dauern, dann ist er noch nicht fertig. Irgendwann wird auf
         dem Server die Datei beginnen, zu existieren; sie wird wachsen und ir-
         gendwann aufhören, zu wachsen. Mit sehr viel Glück liegen Deine Polling-
         Versuche während einer dieser Phase. Mit etwas Pech siehst Du die Datei
         nur plötzlich auf dem Server entstehen.

    Was Du alles nicht sehen kannst, ist:
    a) einen prozentualen Anteil der Übertragung. Niemand weiß nämlich, wie
       groß die Datei am Ende sein wird. Zustandsbalken etc. entfallen also.
    b) ein zuverlässiges Ende der Übertragung. Wenn sich der Zustand der
       Datei nicht mehr ändert, dann kann das Upload-Skript vielleicht nur
       eine Pause machen. Falls sich Deine beiden Skripte auf dem Server aber
       gut genug abgesprochen haben, könnte das Upload-Skript etwa die fertige
       Datei von x.tmp nach x umbenennen - das wäre für den "Poller" eine
       halbwegs brauchbare Information. Und mit dieser kann er sein Polling
       dann auch beenden.

    Aber was Du eigentlich wolltest, geht mit HTTP nicht.
    Dafür bräuchtest Du ein Protokoll, in welchem die Datei häppchenweise
    und mit Rückgabe des Kontrollflusses an den Verursacher hochgeladen wird.
    FTP wäre ein solches - HTTP nicht.

    Viele Grüße
          Michael

    P.S.: Vielleicht fällt Dir auf, daß meine Ausführungen keinerlei Erwähnung
          einer bestimmten serverseitigen Programmiersprache enthielten.

    1. Fein. Der Upload ist inzwischen möglicherweise fertig. Vielleicht auch nicht. Nur: Eine Kontrolle irgend einer Art hast Du darüber nicht.

      Doch. In diesem Falle ist das Tempfile, das PHP _definitv_ erzeugt entweder nicht mehr gelockt (Upload ist beendet, aber das entgegennehmende Script ist noch nicht abgelaufen) oder nicht mehr existent, da es nach Ablauf des Scriptes, das vom Upload-Form aufgerufen wurde, gelöscht wird.

      Wie kommst Du darauf, daß der Upload eine temporäre Datei verwendet?

      1. Es steht im Manual.
      2. PHP übergibt dem Script, das den Upload entgegennimmt, den entsprechenden temporären Dateinamen.

      Das Ziel des "Upload" ist ja nicht, eine Datei auf dem Server zu erzeugen. Das Ziel ist, es auf dem Server eine Anwendung zu starten, welche den Inhalt eines CGI-Multipart-Pakets als Parameter erhält.

      Genau. Im Falle von PHP wird der entsprechende Part eben als temporäre Datei übergeben, d.h. PHP sorgt dafür, dass ein Teil der  ankommenden body-Daten des Requests in ebenjener Datei landet.

      * Gibt es eine Möglichkeit, den Namen des Tempfiles (in Verbindung mit einer Session) im Voraus festzulegen, ohne am PHP Interpreter herumzubosseln?

      Nein. Weder existiert eine solche Datei unbedingt

      Siehe oben...

      noch erlaubt Dir HTTP, darauf in irgend einer Weise Einfluß zu nehmen.

      Hm, ich war wohl etwas undeutlich. Also noch einmal: Es geht - zumindest in dieser Frage - um die Realisierung der im Subject genannten Aufgabe in _PHP_. Und damit kann ich auf einiges Einfluss nehmen (entsprechende Rechte vorausgesetzt).

      * Gibt es eine Möglichkeit, die id des http-Prozesses des Uploads herauszubekommen und darüber das File zu indentifizieren?

      Du gehst immer noch davon aus, daß der Upload etwas sei, was er nicht ist. Passe Deine Vorstellung der Realität an.

      Könntest Du das präzisieren?
      Meine Vorstellung vom Vorgang des Multipart-Requests ist es, dass es auf alle Fälle einen Server-Prozess gibt, der den Request und die zugehörigen Daten entgegennimmt. Dieser Prozess reicht die Daten an die im Request genannte Anwendung bzw. deren Handler - in diesem Falle den PHP Interpreter - weiter. Dieser wiederum erkennt, dass der Request im body Daten enthält und speichert diese in einer temporären Datei zwischen, um deren Namen an das aufgerufene Script zu übergeben, sobald die Daten komplett angekommen sind und der Parser mit der Abarbeitung des Scriptes beginnt.

      Etwas Anderes ist es, wenn Du Dein Modell in mehrere Requests zerlegen könntest.

      Erst lesen, dann posten. Ich zitiere mich mal selbst: "Also wird per onsubmit ein zweites Script aufgerufen[..]". Das _ist_ ein zweiter Request.

      Du könntest also _theoretisch_ folgendes versuchen:

      [..]
      Nichts anderes habe ich bereits beschrieben, wenn auch nicht so ausführlich.

      Wäre JavaScript eine "richtige" Programmiersprache, dann könnte es sogar einen HTTP-HEAD-Request auf die entstehende Datei senden - [..]

      Es geht hier um PHP. Mir ist schleierhaft, wieso dieser Thread in der Rubrik "HTTP" fortgesetzt wird. Die einzige Stelle, an der Javascript vorkommt, ist das Auslösen des zweiten Requests. Natürlich muss das Tracing serverseitig ablaufen, denn dort passiert ja das, was ich tracen möchte.

      [..]Parametern berechnen können, wie die zu erstellende Datei auf dem Server heißen wird.

      Nach eben diesen Parametern habe ich gefragt. Nach nichts anderem, denn der Rest ist trivial.

      a) einen prozentualen Anteil der Übertragung. Niemand weiß nämlich, wie groß die Datei am Ende sein wird. Zustandsbalken etc. entfallen also.

      Der HTTP-Request enthält in der Regel einen Content-Length Header, der von den üblichen Browsern bei Multipart-Daten IMHO auch mitgeschickt wird. Probleme gibt es u.U. damit, von einem paralellen Prozess aus auf diese Daten zuzugreifen ohne root-Rechte zu haben.

      b) ein zuverlässiges Ende der Übertragung. Wenn sich der Zustand der Datei nicht mehr ändert, dann kann das Upload-Script vielleicht nur eine Pause machen.

      Im Regelfall sind solche Dateien offen, d.h. sie haben ein entsprechendes locking, das sich auch abfragen lässt. Davon abgesehen gibt es ein eindeutiges Signal für einen abgeschlossenen Multipart-Request, da dann mit der Abarbeitung des Scriptes begonnen wird und dieses (z.B. über Shared Memory oder Semaphore) dem zweiten Script (also dem Tracer) Bescheid sagen kann.

      P.S.: Vielleicht fällt Dir auf, daß meine Ausführungen keinerlei Erwähnung einer bestimmten serverseitigen Programmiersprache enthielten.

      Das ist mir allerdings aufgefallen, aber was sagt mir das? Genau darauf bezog sich meine Frage, weswegen dieser Thread von mir auch unter der Rubrik "PHP" begonnen wurde und nicht unter "HTTP". Keine Ahnung, wie der hierher geraten ist - war da der Forumsgeist am Werk?

      regards,
      Andreas

      1. Moin

        Wie kommst Du darauf, daß der Upload eine temporäre Datei verwendet?

        1. Es steht im Manual.
        2. PHP übergibt dem Script, das den Upload entgegennimmt, den entsprechenden temporären Dateinamen.

        s.u.

        Du gehst immer noch davon aus, daß der Upload etwas sei, was er nicht ist. Passe Deine Vorstellung der Realität an.

        Könntest Du das präzisieren?
        Meine Vorstellung vom Vorgang des Multipart-Requests ist es, dass es auf alle Fälle einen Server-Prozess gibt, der den Request und die zugehörigen Daten entgegennimmt. Dieser Prozess reicht die Daten an die im Request genannte Anwendung bzw. deren Handler - in diesem Falle den PHP Interpreter - weiter. Dieser wiederum erkennt, dass der Request im body Daten enthält und speichert diese in einer temporären Datei zwischen, um deren Namen an das aufgerufene Script zu übergeben, sobald die Daten komplett angekommen sind und der Parser mit der Abarbeitung des Scriptes beginnt.

        Du hast deine Frage doch schon selbst beantwortet: PHP nimmt die Daten entgegen und speichert sie in einer Datei zwischen, nicht der Server. Aus dem was wir bisher wissen, lässt sich nicht sagen dass der Server die hochgeladenen Daten ebenfalls irgendwo zwischenspeichert, und falls ja, wo und unter welchem Namen.
        Das die Datei generierende PHP wird nämlich erst aufgerufen nachdem alle Daten erfolgreich übertragen wurden, und genau das nützt dir - wenn ich deine Fragestellung richtig verstehe - herzlich wenig.
        Falls du dennoch einen Weg finden solltest dem Server die Infos irgendwie zu entlocken wäre es sehr schön (du könntest dann ja einen Feature-Artikel dazu schreiben, weil ähnliche Anliegen sicherlich mehrere haben), aber meine Vermutung ist, dass es nicht ohne rummfummeln im Quellcode des Apachen gehen wird.

        --
        Henryk Plötz
        Grüße aus Berlin

        1. Du hast deine Frage doch schon selbst beantwortet: PHP nimmt die Daten entgegen und speichert sie in einer Datei zwischen, nicht der Server. Aus dem was wir bisher wissen, lässt sich nicht sagen dass der Server die hochgeladenen Daten ebenfalls irgendwo zwischenspeichert, und falls ja, wo und unter welchem Namen.

          Mich interessiert hier auch nur die Datei, die PHP benutzt. Der Server reicht sie, soweit ich weiss, einfach durch. Wenn PHP als Apache-Modul, also im selben Prozess und mit derselben PID, ist da wohl kein Unterschied. Interessant wäre noch der Fall, bei dem PHP als CGI läuft. Ich nehme aber an, dass der httpd in diesem Fall mit einer Pipe arbeitet, so dass die Daten ohne Verzögerung an die verarbeitende Anwendung weitergereicht werden.

          Falls du dennoch einen Weg finden solltest dem Server die Infos irgendwie zu entlocken wäre es sehr schön (du könntest dann ja einen Feature-Artikel dazu schreiben, weil ähnliche Anliegen sicherlich mehrere haben), aber meine Vermutung ist, dass es nicht ohne rummfummeln im Quellcode des Apachen gehen wird.

          Vom Apache werd ich die Finger lassen, das wäre wohl kaum eine akzeptable Lösung :) Selbst ein externes PHP-Erweiterungsmodul würde nur wenigen etwas nützen, da die meisten auf die PHP Version ihres Providers festgelegt sind.
          Aber falls ich etwas finden sollte, wirds auch veröffentlicht.

          regards,
          Andreas

          1. Moin

            Mich interessiert hier auch nur die Datei, die PHP benutzt. Der Server reicht sie, soweit ich weiss, einfach durch. Wenn PHP als Apache-Modul, also im selben Prozess und mit derselben PID, ist da wohl kein Unterschied. Interessant wäre noch der Fall, bei dem PHP als CGI läuft. Ich nehme aber an, dass der httpd in diesem Fall mit einer Pipe arbeitet, so dass die Daten ohne Verzögerung an die verarbeitende Anwendung weitergereicht werden.

            Genau, aber die Datei ist für deine Zwecke nutzlos.
            Damit ich hier nicht so im luftleeren Raum rumstochere, habe ich eben ein paar Experimente mit Dateiuploads und Apache mit PHP als Modul gemacht.
            Soweit ich das beurteilen kann, läuft der Upload so ab:

            Lese _alle_ Daten die zur Datei gehören vom Client ein
            Erstelle eine temporäre Datei und schreibe die Daten da rein
            Lade das PHP-Skript

            Ich hab zwar nur mit einer Datei von 23 kB rumgespielt, aber ich glaube kaum dass das Verhalten bei größeren Dateien anders aussehen wird (lasse mich aber gern eines besseren belehren).

            Wie du siehst, werden die Daten während sie hochgeladen werden, nirgendwo in eine Datei abgespeichert, du kannst also den Übertragungsvorgang nicht beobachten (naja, ausser du hast root-Rechte und fuhrwerkst im Speicher rum). Die Datei die PHP übergeben kriegt, wird erst erstellt wenn die Daten alle schon da sind. Selbst wenn du also den Namen irgendwie vorher rauskriegen solltest, kannst du immernoch keine Aussage über den Fortschritt des Uploads machen.

            Falls du selbst experimentieren möchtest hier noch eine kurze Anleitung:
            + Erstelle ein PHP-Skript das eine hochgeladene Datei entgegen nimmt sowie ein passendes Upload-Formular und lade das Formular im Browser.
            + Kille den Apache und starte ihn im Debug-Modus über strace ([1]), etwa so
            strace /usr/sbin/httpd -X 2>&1 | tee logdatei
            + Warte bis der Apache bereit ist (dann sollte strace sowas wie accept(16,  evt. mit anderer Zahl ausgeben)
            + Lade eine x-beliebige Datei im Browser hoch.
            + beende strace und den Apachen (Strg-C) und schau dir die generierte Logdatei an.

            Da steht dann etwa sowas drin (ziemlich weit am Ende):
            accept(16, {sin_family=AF_INET, sin_port=htons(4655), sin_addr=inet_addr("127.0.0.1")}}, [16]) = 5
             -- Die Verbindung vom Browser wurde angenommen und hat den Dateideskriptor 5 erhalten.
            read(5, "POST /~henryk/senden.php HTTP/1."..., 4096) = 536
             -- Es wurden 536 Bytes vom Deskriptor 5 (dem Browser) gelesen
             (Wiederholt sich mehrfach, mit anderen Zeilen dazwischen)
            read(5, "e Grafikkarte k\366nnen Sie im n\344ch"..., 4096) = 3683
             -- Es wurden 3683 Bytes vom Browser gelesen. Da aber bis zu 4096 Bytes erwartet wurden, gehe ich davon aus dass der Upload beendet ist
            open("/tmp/php3eSeee", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 7
             -- /tmp/php3eSeee wurden zum exklusiven Schreiben geöffnet (und hat Deskriptor 7 gekriegt)
            write(7, "<BASE HREF="http://sdb.suse.de/d"..., 20480) = 20480
             -- Es wird was (die empfange Daten) in die Temp-Datei geschrieben
            close(7)
             -- Die Tempdatei wird geschlosssen
            open("/home/henryk/public_html/senden.php", O_RDONLY|O_LARGEFILE) = 7
             -- Das PHP-Skript wird geöffnet

            --
            Henryk Plötz
            Grüße aus Berlin