Thomas Möhring: Datei-Upload + zusätzliche Formulardaten in einem Formular

Hallo,

habe schon mehrere Uploads mit Formular und Perl-Auswertung organisiert, aber immer nur eindimensional, d.h. mit enctype="multipart/form-data" und nur einem input-Feld des Typs file und einem submit-Button. Kennt jemand eine Methode, um weitere Formularfeld-Inhalte im gleichen Formular-submit mit zu übertragen und durch das gleiche Script auswerten zu lassen?

Beispiel: Eine Webseite soll upgedatet werden, diese enthält Grafiken, Texte, Überschriften usw. Pro Webseite soll es ein Formular und ein submit geben.

Hat jemand eine Anregung?

Danke im Voraus,
Thomas

  1. habe schon mehrere Uploads mit Formular und Perl-Auswertung organisiert, aber immer nur eindimensional, d.h. mit enctype="multipart/form-data" und nur einem input-Feld des Typs file und einem submit-Button. Kennt jemand eine Methode, um weitere Formularfeld-Inhalte im gleichen Formular-submit mit zu übertragen und durch das gleiche Script auswerten zu lassen?

    Was heißt Mthode? Das ist ohne irgendwelche Methoden möglich. Kann es sein, dass du nicht CGI.pm dafür benutzt?

    Struppi.

    1. Kann es sein, dass du nicht CGI.pm dafür benutzt?

      Ja, so ist es ... verwende

      if($ENV{'REQUEST_METHOD'} eq 'GET')
       {
       $Daten = $ENV{'QUERY_STRING'}
       }
      else
       {
       read(STDIN, $Daten, $ENV{'CONTENT_LENGTH'});
       }

      @Formularfelder = split(/&/, $Daten);

      my %F;
      foreach $Feld (@Formularfelder)
       {
       ($name[$i], $value[$i]) = split(/=/, $Feld);
       $value[$i] =~ tr/+/ /;
       $value[$i] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
       $value[$i] =~ s/<!--(.|\n)*-->//g;
       $value[$i] =~ s/'//g;
       $F{$name[$i]} = $value[$i];
       $i++;
       }

      ... und habe dann alle Daten in %F.

      1. Kann es sein, dass du nicht CGI.pm dafür benutzt?

        Ja, so ist es ... verwende

        Das ist schlecht.

        ... und habe dann alle Daten in %F.

        Nein hast du nicht, du hast immer nur jeweils den letzten Wert

        Struppi.

        1. Nein hast du nicht, du hast immer nur jeweils den letzten Wert

          Was bitte meinst Du damit?

          Natürlich habe ich im Hash %F alle Inhalte aller übertragenen Formularfelder zur Verfügung. Z.B. erhält $F{'textfeld'} den Inhalt aus <textarea name="textfeld">. Das ist übrigens sehr praktisch, um am Ende des Scripts mit einer Zeile wie
          for $temp (keys %F){$datei =~ s/\+$temp+\/$F{$temp}/gi;}
          ein Template ($datei) komplett zu füllen.

          Leider hilft mir diese Diskussion aber nicht weiter, denn die Frage wäre, wie ich alle mit enctype="multipart/form-data" und "post" übertragenen Daten wie bisher sauber in einen Hash einlese. Wenn keiner was weiß, muss ich mir wohl selber was basteln (:-))

          1. Nein hast du nicht, du hast immer nur jeweils den letzten Wert

            Was bitte meinst Du damit?

            Das wenn du mehrere Felder mit den gleichen Namen hast (was nicht bei mir häufiger vorkommt) du immer nur den Wert des letzten Feldes bekommst.

            Das ist übrigens sehr praktisch, um am Ende des Scripts mit einer Zeile wie
            for $temp (keys %F){$datei =~ s/\+$temp+\/$F{$temp}/gi;}
            ein Template ($datei) komplett zu füllen.

            HTML::Template arbeitet super mit CGI zusammen, du solltest dir mal die Module die auf deinem Rechner installiert sind anschauen, es ist gängige Praxis (sowohl in Perl als auch in fast allen anderen Programmiersprachen, das man Libaries oder Module, die i.d.R. jahrelange Praxiserprobung hinter sich haben, zu benutzen, dies nicht zu machen ist eher kein guter Stil)

            Leider hilft mir diese Diskussion aber nicht weiter, denn die Frage wäre, wie ich alle mit enctype="multipart/form-data" und "post" übertragenen Daten wie bisher sauber in einen Hash einlese. Wenn keiner was weiß, muss ich mir wohl selber was basteln (:-))

            use CGI;
            und alle deine Probleme (auch die du noch bekommst mit deinem bisherigen Ansatz) sind beseitigt.

            Struppi.

            1. (was nicht bei mir häufiger vorkommt)

              (was bei mir häufiger vorkommt)

              Struppi.

              1. (was bei mir häufiger vorkommt)

                Das finde ich nun wieder weniger guten Stil ...

                Thomas.

                1. (was bei mir häufiger vorkommt)

                  Das finde ich nun wieder weniger guten Stil ...

                  So pauschal kann man das nicht sagen, wie löst du es wenn du eine Liste von Datensätzen hast und dort z.b. das Feld Name ändern willst:

                  #1 <id> <name>
                  #2 <id> <name>
                  #3 <id> <name>

                  Struppi.

            2. use CGI;
              und alle deine Probleme (auch die du noch bekommst mit deinem bisherigen Ansatz) sind beseitigt.

              Dafür kriege ich dann andere (:-)). Aber ist ok, danke. Andere Module nutze ich ja auch viel und gerne.

              1. use CGI;
                und alle deine Probleme (auch die du noch bekommst mit deinem bisherigen Ansatz) sind beseitigt.

                Dafür kriege ich dann andere (:-)). Aber ist ok, danke. Andere Module nutze ich ja auch viel und gerne.

                Falls du unbedingt dein %F weiter verwenden willst geht das auch mit dem Modul.

                my %F = CGI::Vars();

                Struppi.

                1. Moin Struppi,

                  Falls du unbedingt dein %F weiter verwenden willst geht das auch mit dem Modul.

                  my %F = CGI::Vars();

                  Ist da auch der Filehandler drin, der dann mit der read() Funktion ausgelesen werden kann, um das File auf den Server zu schreiben?

                  Viele Grüße,
                     Ernst

                  1. Hey Rolf.

                    my %F = CGI::Vars();
                    Ist da auch der Filehandler drin, der dann mit der read() Funktion ausgelesen werden kann, um das File auf den Server zu schreiben?

                    Ja, wenn eine gültige Datei übergeben wurde. Wenn nicht, steht da drin, was der User in das Feld eingegeben hat. Das empfohlene Verfahren ist $cgi->upload('upload_field').

                    Siehe http://perldoc.perl.org/CGI.html#CREATING-A-FILE-UPLOAD-FIELD

                    Siechfred

                    1. my %F = CGI::Vars();
                      Ist da auch der Filehandler drin, der dann mit der read() Funktion ausgelesen werden kann, um das File auf den Server zu schreiben?
                      Ja, wenn eine gültige Datei übergeben wurde.

                      Gerade getestet: Bei meiner CGI-Version steht nur der Dateiname drin. Einen Filehandler erhält man nur bei Aufruf von CGI::param (unsicher) bzw. bei CGI::upload (empfohlen).

                      Siechfred

                      1. Danke für die Info,

                        Viele Grüße,
                           Ernst

      2. Kann es sein, dass du nicht CGI.pm dafür benutzt?
        Ja, so ist es ... verwende

        Sorry, aber das ist Steinzeit-Perl.

        Mal einen eleganten Weg:

        Das Formular:

        <form name="uploader" action="/cgi-bin/the_script.pl" method="post" enctype="multipart/form-data">  
          <p>Your name: <input type="text" name="name" value=""></p>  
          <p>Your comment: <input type="text" name="comm" value=""></p>  
          <p>The file: <input type="file" name="the_file"></p>  
          <p><input type="submit" value="submit"></p>  
        </form>
        

        Und das Script:

        use strict;  
        use diagnostics;  
        use CGI;  
          
        my $cgi = CGI->new;  
        my $file = $cgi->upload('the_file');  
        if (!$file) {  
          print $cgi->header(-status=>$CGI::cgi_error);  
          exit 0;  
        }  
        print $cgi->header( -type => 'text/html', -charset => 'iso-8859-1' ),  
              $cgi->start_html( -title => 'Uploadform' );  
        if ($cgi->uploadInfo($file)->{'Content-Type'} ne 'text/plain') {  
          print $cgi->h1('Error'),  
                $cgi->p('Text files only');  
          exit 0;  
        }  
        print $cgi->h1('Results'),  
              $cgi->p('Uploaded by: ', $cgi->param('name')),  
              $cgi->p('Comment: ', $cgi->param('comm')),  
              $cgi->start_pre;  
        print $_ while(<$file>);  
        print $cgi->end_pre;
        

        So, und nun erklär' mir mal, wo das Problem ist. Oder denkst du an mehrere Uploads auf einen Ritt?

        Siechfred

        1. Hi Siechfred,

          Deine Lösung sieht auf den ersten Blick gut aus, mich stört aber die Vermischung von Verarbeitung und Ausgabe, was mich grundsätzlich auch an (den meisten Lösungen von) php stört. Daher halte ich alle auszugebenden Fragmente als HTML-Templates vor, die der Designer ohne Programmierkenntnisse schnell ändern kann. Die Verarbeitung der Variablen findet in Perl statt, ausgegeben werden die gefüllten Templates. Nenn es Steinzeit-Perl, es hat sich bewährt.

          Daher suche ich weiter nach einer Möglichkeit, mit %F zu arbeiten - auch beim Upload. Interessant wäre die Antwort auf die Frage von Struppi, ob bei my %F = CGI::Vars(); auch der Filehandler drin ist, der dann mit der read() Funktion ausgelesen werden kann, um das File auf den Server zu schreiben.

          Thomas.

          1. mich stört aber die Vermischung von Verarbeitung und Ausgabe, was mich grundsätzlich auch an (den meisten Lösungen von) php stört. Daher halte ich alle auszugebenden Fragmente als HTML-Templates vor, die der Designer ohne Programmierkenntnisse schnell ändern kann.

            Es ist kein Problem, die Ausgaben in meinem Beispiel mit einer Template-Engine zu realisieren, ich persönlich ziehe da HTML::Template vor.

            Übrigens, wenn du unbedingt dein %F brauchst, ist das mit dem CGI-Modul ein Zweizeiler:

            my %F;  
            $F{$_} = $cgi->param($_) foreach($cgi->param);
            

            Na, besser? ;)

            Siechfred

            1. mich stört aber die Vermischung von Verarbeitung und Ausgabe, was mich grundsätzlich auch an (den meisten Lösungen von) php stört. Daher halte ich alle auszugebenden Fragmente als HTML-Templates vor, die der Designer ohne Programmierkenntnisse schnell ändern kann.

              Es ist kein Problem, die Ausgaben in meinem Beispiel mit einer Template-Engine zu realisieren, ich persönlich ziehe da HTML::Template vor.

              Das sich, wie schon erwähnt, Prima mit dem CGI Modul ergänzt.

                my $query = new CGI;  
                my $template = HTML::Template->new(filename => 'template.tmpl',  
                                                   associate => $query);  
              
              ~~~(aus der [Doku](http://search.cpan.org/dist/HTML-Template/Template.pm))  
                
              
              > Übrigens, wenn du unbedingt dein %F brauchst, ist das mit dem CGI-Modul ein Zweizeiler:  
              >   
              > ~~~perl
              
              my %F;  
              
              > $F{$_} = $cgi->param($_) foreach($cgi->param);
              
              

              oder ein Einzeiler:
              my %F = CGI::Vars();

              hatten wir aber schon.

              Struppi.

              1. Übrigens, wenn du unbedingt dein %F brauchst, ist das mit dem CGI-Modul ein Zweizeiler:

                my %F;

                $F{$} = $cgi->param($) foreach($cgi->param);

                
                > oder ein Einzeiler:  
                > `my %F = CGI::Vars();`{:.language-perl}  
                  
                CGI::Vars liefert \*keinen\* Filehandler, sondern nur den Namen als String. Willst du einen Filehandler im Hash haben, brauchst du zwingend CGI::param bzw. CGI::upload.  
                  
                Siechfred
                
                -- 
                [\[NaN\]](http://www.zenelectrics.de/musik.htm) ~ [\[Somethin' Stupid\]](http://www.stupidedia.org/)
                
                1. Übrigens, wenn du unbedingt dein %F brauchst, ist das mit dem CGI-Modul ein Zweizeiler:

                  my %F;

                  $F{$} = $cgi->param($) foreach($cgi->param);

                  
                  > > oder ein Einzeiler:  
                  > > `my %F = CGI::Vars();`{:.language-perl}  
                  >   
                  > CGI::Vars liefert \*keinen\* Filehandler, sondern nur den Namen als String. Willst du einen Filehandler im Hash haben, brauchst du zwingend CGI::param bzw. CGI::upload.  
                    
                  OK, du magst recht haben, aber ich vermute mal der OP hat vorher %F für sein Templatesystem oder sowas genutzt und mag nicht dies nicht ändern. Der Filehandler dürfte dort deplaziert sein.  
                    
                  Struppi.
                  
                  1. CGI::Vars liefert *keinen* Filehandler, sondern nur den Namen als String. Willst du einen Filehandler im Hash haben, brauchst du zwingend CGI::param bzw. CGI::upload.
                    OK, du magst recht haben, aber ich vermute mal der OP hat vorher %F für sein Templatesystem oder sowas genutzt und mag nicht dies nicht ändern. Der Filehandler dürfte dort deplaziert sein.

                    Hm, er hat aber explizit danach gefragt, ob der Filehandler dann in seinem Hash ist. Oder ich habe ihn falsch verstanden, kann ja auch sein ...

                    Siechfred

                    1. Hm, er hat aber explizit danach gefragt, ob der Filehandler dann in seinem Hash ist. Oder ich habe ihn falsch verstanden, kann ja auch sein ...

                      Nein stimmt. Das ist allerdings merkwürdig.

                      Struppi.

              2. oder ein Einzeiler:
                my %F = CGI::Vars();

                Funktioniert nicht, wenn im <form>-tag 'enctype="multipart/form-data"' übergeben wird, was für den upload vorausgesetzt wird. Mithin behelfe ich mich leider weiterhin mit einem workaround ...

                Danke aber für manche Anregung.

                Thomas.

                1. oder ein Einzeiler:
                  my %F = CGI::Vars();

                  Funktioniert nicht, wenn im <form>-tag 'enctype="multipart/form-data"' übergeben wird, was für den upload vorausgesetzt wird. Mithin behelfe ich mich leider weiterhin mit einem workaround ...

                  Jaja, das hatten wir auch schon diskutiert (Siechfred hat eine andere Mthode vorgeschlagen), aber die Frage war ja ob du den Upload Parameter wirklich in %F brauchst oder ob du nicht einfach die von Siechfred hier verlinkte Methode zum Upload benutzen kannst.

                  Struppi.

  2. habe schon mehrere Uploads mit Formular und Perl-Auswertung organisiert, aber immer nur eindimensional, d.h. mit enctype="multipart/form-data" und nur einem input-Feld des Typs file und einem submit-Button. Kennt jemand eine Methode, um weitere Formularfeld-Inhalte im gleichen Formular-submit mit zu übertragen und durch das gleiche Script auswerten zu lassen?

    Wo soll da ein Problem bestehen? Die Daten anderer Eingabefelder werden genauso übertragen wie die Daten des Dateiauswahlfeldes. Mit Hilfe des CGI-Moduls kannst du diese dann elegant auswerten. Niemand verbietet dir, in einem Script neben einem Dateiupload andere Aktionen mit den übrigen übergebenen Formularfeldern anzustellen.

    Siechfred