oli69: xmlhttprequest file upload aus string

Hallo,
ich habe ein "kleines" Problem bei dem ich trotz google-Befragung nicht weiterkomme.
Es geht um eine Werkzeugmaschine die über einfache Textdateien gesteuert wird. Die Datei besteht nur aus einer Zeile mit 22 Werten die jeweils mit einem ~ beendet werden.
Die Datei wird mit einem Editor erzeugt und über ein einfaches HTML-Formular welches xmlhttprequest verwendet auf die Maschine geladen. Das Formular ist zugänglich und kann verändert werden (per FTP). Idee ist hier jetzt ein Formular mit entsprechenden select's zu verwenden und den String mit Javascript zusammen zu setzen und dann hochzuladen. Die Eingabefelder und das Javascript sind auch kein Problem aber wie bekomme ich den String so das ich ihn als File hochladen kann?
Das Upload-Formular sieht original so aus:

<form action='http://10.1.10.80' method='post' enctype='multipart/form-data' onsubmit='display()'>
 <input type='hidden' name='device' value='627724'>
 <input type='hidden' name='status' value='0'>
 File <input type='file' name='file'>
 <input type='submit' value='Import job'>
</form>​

Der String wäre in der Variablen kommando

Nach dem Übertragen der Datei ruft die Seite dann über xmlhttprequest verschiedene Werte ab und bereitet diese mit Javascript zur Anzeige auf.

Danke.

  1. Lieber oli69,

    was hindert Dich daran, mittels serverseitiger Scriptsprache einen mittels POST (oder von mir aus auch GET) übergebenen String in eine Datei zu schreiben? Bietet euer lokaler Rechner (die IP-Adresse sieht mir doch seeeehr nach "Intranet" aus!) denn keine serverseitige Scriptsprache an? Was läuft denn da als Webserver (Apache?) und auf welchem OS?

    Dateiuploads mittels XmlHttpRequest (auch unter dem Schlagwort "AJAX" zu finden) sind prinzipbedingt nicht möglich, da JavaScript nicht auf lokale Dateien zugreifen darf. Inwiefern Du aber einen Datenstrom im MIME-Format simulieren kannst, den der Server wie einen Dateiupload versteht, übersteigt mein Wissen.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Lieber oli69,

      was hindert Dich daran, mittels serverseitiger Scriptsprache einen mittels POST (oder von mir aus auch GET) übergebenen String in eine Datei zu schreiben? Bietet euer lokaler Rechner (die IP-Adresse sieht mir doch seeeehr nach "Intranet" aus!) denn keine serverseitige Scriptsprache an? Was läuft denn da als Webserver (Apache?) und auf welchem OS?

      Dateiuploads mittels XmlHttpRequest (auch unter dem Schlagwort "AJAX" zu finden) sind prinzipbedingt nicht möglich, da JavaScript nicht auf lokale Dateien zugreifen darf. Inwiefern Du aber einen Datenstrom im MIME-Format simulieren kannst, den der Server wie einen Dateiupload versteht, übersteigt mein Wissen.

      Liebe Grüße,

      Felix Riesterer.

      Hallo Felix,
      ich kann auf der Maschine nur einen Ordner beschreiben, dort sind die .htm-Dateien abgelegt. An den cgi-Bereich komme ich nicht heran da der auf einer Flash-Disk sitzt und der Hersteller das als grosses Geheimnis behandelt. Für Updates bekommen wir immer
      ein File das dann hochgeladen wird und wohl den kompletten Flash-Bereich überschreibt. Man könnte sowas vom Hersteller programmieren lassen, aber das ist aufwendig und auch teuer. Aber ich werde das von hotti angegebene mal ausprobieren.

  2. hi,

    [..] aber wie bekomme ich den String so das ich ihn als File hochladen kann?

    Du erstellst aus dem String einen Blob und verwendest das FormData-Objekt (enctype="multipart/form-data"). Letzteres fügt, weil Du einen Blob übergibtst, automatisch den Parameter filename in den Header der Multipart-Komponente, so erkennt der serverseitige Prozess, dass eine Datei hochgeladen wurde.

    Horst

    1. Lieber hotti,

      ich werde aus Deiner Antwort nur bedingt schlau.

      Du erstellst aus dem String einen Blob

      wie?

      und verwendest das FormData-Objekt (enctype="multipart/form-data").

      Du meinst, ich füge dem <form>-Element das Attribut "enctype" mit dem Wert "multipart/form-data" hinzu? <form method="POST" enctype="multipart/form-data" action="...">

      Letzteres fügt, weil Du einen Blob übergibtst,

      Wie "übergebe" ich diesen ominösen Blob? Was genau tue ich im DOM?

      automatisch den Parameter filename in den Header der Multipart-Komponente,

      Mit welchem Wert?

      so erkennt der serverseitige Prozess, dass eine Datei hochgeladen wurde.

      Klingt nach Magie so wie Du das beschreibst - Heilsversprechen ohne echte Zutaten.

      Liebe Grüße,

      Felix Riesterer.

      --
      ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
      1. Du erstellst aus dem String einen Blob

          
        var myblob = new Blob(['asdf']);  
        
        

        Du meinst, ich füge dem <form>-Element das Attribut "enctype" mit dem Wert "multipart/form-data" hinzu? <form method="POST" enctype="multipart/form-data" action="...">

        Nein, brauchst Du nicht. Du hast ja ein FormData-Objekt, wenn Du das sendest (POST) ist der enctype automatisch multipart/form-data

        Letzteres fügt, weil Du einen Blob übergibtst,

        Wie "übergebe" ich diesen ominösen Blob? Was genau tue ich im DOM?

          
        var form_data = new FormData();  
        form_data.append('myblob_filename', myblob);  
        xhr.params = form_data;  
        // und dann den POST Request  
        
        

        automatisch den Parameter filename in den Header der Multipart-Komponente,

        Mit welchem Wert?

        Guck dir bitte an, wie eine multipart-message aussieht. Sie besteht aus mehreren Komponenten und jede dieser Komponenten hat einen Header, z.B.:

        -----------------------------239462978514022
        Content-Disposition: form-data; name="myblob_filename"; filename="blob"

        name ist das was Du in form_data.append('myblob_filename', myblob) angibst, myblob ist der blob.

        Klingt nach Magie so wie Du das beschreibst - Heilsversprechen ohne echte Zutaten.

        Du kannst das alles auf MDN selbst nachlesen, versprochen wird da garnix, da wird nur beschrieben wie das funktioniert.

        Wenn Du das nachvollziehen willst, einfach mal machen ;)

        1. var form_data = new FormData();
          form_data.append('myblob_filename', myblob);
          xhr.params = form_data;
          // und dann den POST Request

            
          xhr.params? Was soll das sein? Mir nicht bekannt.  
            
          Das FormData-Objekt übergibt man einfach an die send-Methode.  
            
          `xhr.send(form_data);`{:.language-javascript}  
            
          @oli69, Felix:  
          <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects>  
          <http://www.html5rocks.com/de/tutorials/file/xhr2/#toc-send-formdata>  
          <http://new-bamboo.co.uk/blog/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata>  
          <https://gist.github.com/ebidel/2410898>  
            
          Mathias
          
          1. Lieber molily,

            @oli69, Felix:
            https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects
            http://www.html5rocks.com/de/tutorials/file/xhr2/#toc-send-formdata
            http://new-bamboo.co.uk/blog/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata
            https://gist.github.com/ebidel/2410898

            vielen Dank! Man lernt ja bekanntlich nie aus. XHR2... Was es nicht alles gibt! Hier ist noch die Browserunterstützung: http://caniuse.com/xhr2

            Liebe Grüße,

            Felix Riesterer.

            --
            ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
            1. vielen Dank! Man lernt ja bekanntlich nie aus. XHR2... Was es nicht alles gibt! Hier ist noch die Browserunterstützung: http://caniuse.com/xhr2

              Nochwas zu dieser Sache:
              -----------------------------239462978514022
              Content-Disposition: form-data; name="parametername"; filename="blob"

              Das ist ein Teil einer Multipart-Message, die einzelnen Komponenten sind über eine boundary getrennt und jede Komponente hat einen eigenen Header, den Content betreffend. Zum besseren Verständnis habe ich hier eine Demo, wo eine komplette multipart-message zeigt.

              -----------------------------103912343730859
              Content-Disposition: form-data; name="name"

              Lastname

              -----------------------------103912343730859
              Content-Disposition: form-data; name="vname"

              Firstname

              -----------------------------103912343730859--

              Wie Du siehst, im jeweiligen Header fehlt der Parameter filename. Genau das ist der Knackpunkt, woran der Serverprozess festmacht, ob ein Upload stattgefunden hat. Nun, diesen Parameter filename="blob" fügt FormData genau dann hinzu, wenn aus den Eingaben ein Blob erstellt wurde oder der Content aus einem Formularfeld

              <input type="file" name="ein mir gerechter parametername">

              geliefert wurde. In meiner Demo wurde jedoch kein Blob erstellt, deswegen fehlt filename="blob". Bei einem herkömmlichen Upload schließlich, ist der Browser dafür zusändig was in filename="???" als Wert reinkommt.

              Horst

              1. Hallo Rolf,

                ich muss mal nachfragen: kann ich, so wie du beschrieben hast, Daten hoch laden ohne CGI-Technik auf dem Server? Oder habe ich die Frage des TO nur falsch verstanden?

                Gruß, Jürgen

                1. hi Jürgen,

                  ich muss mal nachfragen: kann ich, so wie du beschrieben hast, Daten hoch laden ohne CGI-Technik auf dem Server? Oder habe ich die Frage des TO nur falsch verstanden?

                  Danke Deiner Nachfrage.

                  Den Serverprozess brauchst Du auf jeden Fall. FormData erzeugt einen Enctype Multipart als enctype="multipart/form-data", das ist ein Standard, den alle Browser unterstützen (IE übrigens seit 1997 in Version 3.2, wenn ich mich recht erinnere *g*).

                  Die Frage des TO zielt darauf ab, an eben diesem Serverprozess nichts ändern zu müssen.

                  Das neue JS-Objekt FormData erweitert das, was Browser bisher können:

                  <form action="%url%" method="POST" enctype="multipart/form-data">  
                  <input type="file">
                  

                  so, dass es nunmehr auch mit JS/XHR geht, ohne dass am serverseitige Prozess was geändert werden muss: In dem Moment, wo im Komponenten-Header der Parameter filename="asdf" notiert ist, wird auf dem Server eine Datei angelegt (PHP wie Perl).

                  Rolf

                2. Hallo,

                  kann ich, so wie du beschrieben hast, Daten hoch laden ohne CGI-Technik auf dem Server?

                  Nein, es ist immer eine Server-Software nötig, die mit dem Request umgeht. Einen Automatismus in Webservern, dass Dateien immer angenommen und gespeichert werden, gibt es nicht.

                  In Falle von oil69 scheint wohl eine entsprechende Server-Software zu existieren.

                  Hochladen ohne CGI-Technik war einmal mit der HTTP-PUT-Methode gedacht, allerdings lassen das heute die wenigsten Webserver zu. Heute braucht man wohl selbst dafür CGI.

                  Mathias

                  1. Hallo molily,

                    kann ich, so wie du beschrieben hast, Daten hoch laden ohne CGI-Technik auf dem Server?

                    Nein, ...

                    dann habe ich nichts verpasst. Das ist auch mein Wissensstand.

                    Gruß, Jürgen

                    1. Hello,

                      kann ich, so wie du beschrieben hast, Daten hoch laden ohne CGI-Technik auf dem Server?

                      Nein, ...

                      dann habe ich nichts verpasst. Das ist auch mein Wissensstand.

                      Es fehlt schlichtweg ein Handler für die Mehtode.
                      http://www.apacheweek.com/features/put

                      Wie Du den realisierst, ist Dir überlassen.

                      Liebe Grüße aus dem schönen Oberharz

                      Tom vom Berg

                      --
                       ☻_
                      /▌
                      / \ Nur selber lernen macht schlau
                      http://bikers-lodge.com
              2. Hallo,

                Nun, diesen Parameter filename="blob" fügt FormData genau dann hinzu, wenn aus den Eingaben ein Blob erstellt wurde oder der Content aus einem Formularfeld

                Ich verstehe leider nicht, was du mit diesem Posting sagen willst. Der Dateiname lässt sich beim append() angeben, wenn ein Blob übergeben wird:
                void append(DOMString name, Blob value, optional DOMString filename);
                https://developer.mozilla.org/en-US/docs/Web/API/FormData#append()

                Ich denke aber nicht, dass hier ein Blob nötig ist. Wenn ich einfach das Formular abschicken will, kann ich das mit FormData tun. http://www.html5rocks.com/de/tutorials/file/xhr2/#toc-send-formdata habe ich schon verlinkt.

                Soweit ich oli69 verstanden habe, will er die Datei nicht clientseitig lesen und dann manuell einen Request zusammenbauen, sondern das Formular absenden, wie es ist – inklusive File-Upload. Der Browser sollte den Rest von selbst machen.

                Mathias

                1. hi,

                  Ich verstehe leider nicht, was du mit diesem Posting sagen willst. Der Dateiname lässt sich beim append() angeben, wenn ein Blob übergeben wird:
                  void append(DOMString name, Blob value, optional DOMString filename);
                  https://developer.mozilla.org/en-US/docs/Web/API/FormData#append()

                  Ok,

                  #1 void append(DOMString name, File value, optional DOMString filename); // default filename="browserabhängig"
                  #2 void append(DOMString name, Blob value, optional DOMString filename); // default filename="blob"
                  #3 void append(DOMString name, DOMString value);                         // kein Parameter filename

                  sieht so aus:

                  -----------------------------239462978514022
                  Content-Disposition: form-data; name="DOMString name"; filename="DOmString filename"

                  #1 File value

                  -----------------------------239462978514022
                  Content-Disposition: form-data; name="DOMString name"; filename="DOMString filename"

                  #2 Blob value

                  -----------------------------239462978514022
                  Content-Disposition: form-data; name="DOMString name";

                  #3 DOMString value

                  -----------------------------239462978514022--

                  Ich denke aber nicht, dass hier ein Blob nötig ist.

                  Doch, genau das ist. Wenn nämlich nicht, hast Du #3 heißt: Kein Upload, weil Parameter filename gar nicht gestzt wird.

                  Soweit ich oli69 verstanden habe, will er die Datei nicht clientseitig lesen und dann manuell einen Request zusammenbauen, sondern das Formular absenden, wie es ist – inklusive File-Upload. Der Browser sollte den Rest von selbst machen.

                  Genau. Und ich finde, die Oli69 Idee ist gar nicht mal so neu ;)

                  1. Ich denke aber nicht, dass hier ein Blob nötig ist.

                    Doch, genau das ist. Wenn nämlich nicht, hast Du #3 heißt: Kein Upload, weil Parameter filename gar nicht gestzt wird.

                    Ich rede davon, entweder #1 zu verwenden (File-Objekt übergeben) oder besser gar nicht manuell append() aufzurufen.

                    oli69 hat bereits ein <input type="file">. Siehe Ausgangsposting. Es muss einfach das ganze Formular abgeschickt werden. Das geht ohne append, einfach mit new FormData(form).

                    Beispiel: http://codepen.io/molily/pen/vACKq?editors=101

                    Das Formular wird mit JavaScript genauso abgesendet wie ohne JavaScript, der Request ist der gleiche. (Getestet in Chrome.)

                    Das ist doch viel einfacher, als das File des <input type="file"> zu lesen, den Inhalt mit einem FileReader zu lesen, in einen Blob zu verpacken und an das FormData zu hängen.

                    Mathias

                    1. oli69 hat bereits ein <input type="file">. Siehe Ausgangsposting. Es muss einfach das ganze Formular abgeschickt werden.

                      (Sofern ich oli69 richtig verstanden habe – ich kann mich natürlich irren.)

                      Mathias

                      1. oli69 hat bereits ein <input type="file">. Siehe Ausgangsposting. Es muss einfach das ganze Formular abgeschickt werden.

                        (Sofern ich oli69 richtig verstanden habe – ich kann mich natürlich irren.)

                        Hat er ;)

                        Und so hab ichs verstanden:

                        Das ist die bisherige Lösung.

                        Und so hab ichs weiterhin verstanden:

                        Eine neue Lösung muss her und soll so beschaffen sein, dass anstelle einer Datei-Erstelling und Upload dieser Datei, die Eingaben gleich im Browser zusammengestellt werden sollen.

                        Damit am Serverprozess nichts geändert werden muss, isses erforderlich, ein Upload zu simulieren: Yes, we can!

                        Der Serverprozess konstatiert ein Upload anhand des Komponenten-Headers mit dem Parameter filename=

                        Content-Disposition: form-data; name="DOMString name"; filename="DOMString filename"

                        wobei der Header-Parameter filename="blob" oder filename="nasty.blast" nur dann gesetzt wird, wenn FormData.append() einen Blob bekommt. Und dieser Blob lässt sich aus gewöhnlichen Inputfeldern zusammenbauen.

                        Btw., im Komponenten-Header see above, fehlt die Angabe Content-Type. Den können wir auch liefern:

                        var myblob = new Blob(['asdf'], { type: 'text/plain; charset=us-ascii' });

                        Grüße an den REST der Welt ;)

                    2. Ich denke aber nicht, dass hier ein Blob nötig ist.

                      Doch, genau das ist. Wenn nämlich nicht, hast Du #3 heißt: Kein Upload, weil Parameter filename gar nicht gestzt wird.

                      Ich rede davon, entweder #1 zu verwenden (File-Objekt übergeben) oder besser gar nicht manuell append() aufzurufen.

                      oli69 hat bereits ein <input type="file">. Siehe Ausgangsposting. Es muss einfach das ganze Formular abgeschickt werden. Das geht ohne append, einfach mit new FormData(form).

                      Beispiel: http://codepen.io/molily/pen/vACKq?editors=101

                      Das Formular wird mit JavaScript genauso abgesendet wie ohne JavaScript, der Request ist der gleiche. (Getestet in Chrome.)

                      Das ist doch viel einfacher, als das File des <input type="file"> zu lesen, den Inhalt mit einem FileReader zu lesen, in einen Blob zu verpacken und an das FormData zu hängen.

                      Mathias

                      Könnte ich hierzu mal ein kurzes Beispiel bekommen? Z.B. mit String:
                      var daten = '0.5~0.5~10~';   stark gekürzt, sieht im Original aber so aus.

                      ???????

                      <form action='http://10.1.10.80' method='post' enctype='multipart/form-data' onsubmit='display()'>
                       <input type='hidden' name='device' value='627724'>
                       <input type='hidden' name='status' value='0'>
                       File <input type='file' name='file'>
                       <input type='submit' value='Import job'>
                      </form>​

                      Ich würde dann gerne das File-Feld ebenfalls hidden machen und bei onsubmit eine Funktion aufrufen die den String einfügt und absendet und danach die Funktion display() aufruft. Aber irgendwie stehe ich im Moment auf dem Schlauch....

                      Gruss Oliver

                      1. Könnte ich hierzu mal ein kurzes Beispiel bekommen? Z.B. mit String:
                        var daten = '0.5~0.5~10~';   stark gekürzt, sieht im Original aber so aus.

                        var formData = new FormData();  
                          
                        // Datei anhängen  
                        var blob = new Blob([daten], { type: 'text/plain' });  
                        formData.append('file', blob, 'filename.txt');  
                          
                        // Weitere Parameter anhängen  
                        formData.append('device', '627724');  
                        formData.append('status', '0');  
                        // usw.  
                          
                        var xhr = new XMLHttpRequest();  
                        xhr.open('POST', '/url', true);  
                        xhr.onload = function(event) { console.log('load'); /* … */ };  
                        xhr.onerror = function(event) { console.log('error'); /* … */ };  
                        xhr.send(formData);
                        

                        (Bring nur zusammen, was bereits gepostet wurde.)

                        Mathias

                        1. Könnte ich hierzu mal ein kurzes Beispiel bekommen? Z.B. mit String:
                          var daten = '0.5~0.5~10~';   stark gekürzt, sieht im Original aber so aus.

                          var formData = new FormData();

                          // Datei anhängen
                          var blob = new Blob([daten], { type: 'text/plain' });
                          formData.append('file', blob, 'filename.txt');

                          // Weitere Parameter anhängen
                          formData.append('device', '627724');
                          formData.append('status', '0');
                          // usw.

                          var xhr = new XMLHttpRequest();
                          xhr.open('POST', '/url', true);
                          xhr.onload = function(event) { console.log('load'); /* … / };
                          xhr.onerror = function(event) { console.log('error'); /
                          … */ };
                          xhr.send(formData);

                          
                          >   
                          > (Bring nur zusammen, was bereits gepostet wurde.)  
                          >   
                          > Mathias  
                            
                          Hallo,  
                          klasse und danke. So geht es tatsächlich. Allerdings musste ich den Code ändern, wie oben lief es bei mir nicht, scheinbar ist es wichtig das formData.append('file' ....  
                          das letzte append ist. Andere Konstellationen gingen nicht.
                          
                          1. hi,

                            klasse und danke. So geht es tatsächlich. Allerdings musste ich den Code ändern, wie oben lief es bei mir nicht, scheinbar ist es wichtig das formData.append('file' ....
                            das letzte append ist. Andere Konstellationen gingen nicht.

                            Übergibst Du das Formular beim Erstellen des FormData-Objekts? Dann sollte das wirklich hintendran, denn es überschreibt Dein möglicherweise noch vorhandenens input-type-file-field. Ich habe meine Demo mal erweitert, jetzt ist das alles auch zu sehen (nur noch nicht dokumentiert).

                            --
                            Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.