http://www.perlmonks.org?node_id=1184466

edimusrex has asked for the wisdom of the Perl Monks concerning the following question:

I've seen this question asked a few times and yet I haven't found a solution so I am going to ask again (specific to my question).

I have a simple web form in which I want the user to select a folder to upload (I only care about the contents of the folder). So my attempt to make this work is as followed:

#!/usr/bin/perl use strict; use warnings; use CGI; use Data::Dumper; my $q = CGI->new(); print $q->header; my $upload_folder = '/var/www/html/uploads'; my $name = $q->param('name'); my $email = $q->param('email'); my $comments = $q->param('comments'); my @files = $q->param('multi_files'); foreach my $upload(@files){ print "Upload this please -- $upload<br>"; my $upload_file = $q->upload($upload); if ($upload_file){ open (OUTFILE,">$upload_folder/$upload") or die $!;; binmode OUTFILE; while (<$upload_file>) { print OUTFILE; } } else { print "<b>Guess it's broken</b><br/>"; } } print "<p><b>Name</b> -- $name<br><b>Email</b> -- $email<br><b>Comment +s</b> -- $comments<br>"; print $q->end_html;
When I run the script all the parameters are correct, the files print out as expected but when I execute the upload query it returns blank. This is my first attempt at using CGI as I tend to use other languages to process forms.

Here is the code from the form just in case :

<html> <head> <title>Stupid Test Site</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jqu +ery.min.js" ></script> </head> <body> <h1>This is a test</h1> <form action="/cgi-enabled/test.pl" method="POST"> Your Name: <input type="text" name="name"><br> Email Address: <input type="text" name="email"><br> Select PO folder: <input name="multi_files" type="file" we +bkitdirectory multiple /><br/> Comments:<br> <textarea name="comments" rows="5" cols="60"></textarea><b +r> <input type="submit" value="Send"> </form> </body> </html>
Any help on this issue would be greatly appreciated.

I've also posted this question on stackoverflow.com, sorry for the cross post --


----- Update -----


My error was in fact due to the missing enctype="multipart/form-data" in the form. I made some changes and tweaked some things based on all of you input. Here is my final working code :
#!/usr/bin/perl use strict; use warnings; use CGI; use Data::Dumper; use CGI::Carp qw( fatalsToBrowser ); use HTML::Entities qw/encode_entities/; use File::Copy qw' copy '; use File::Basename; my $q = new CGI; print $q->header; my $upload_folder = '/var/www/html/uploads'; my $name = $q->param('name'); my $email = $q->param('email'); my $comments = $q->param('comments'); my @files = $q->param('multi_files'); my @io_handles=$q->upload('multi_files'); my %file_hash; foreach my $item(@files){ next unless $item =~ /.+\/(M.+\.pdf)/; foreach my $sub_item(@io_handles){ if($item eq $sub_item){ $file_hash{$item} = $sub_item; } } } chdir $upload_folder or die "Cannot chdir to upload destination direct +ory: $!\n"; print '<ul>'; foreach my $key(keys %file_hash){ my $base = basename($file_hash{$key});# my $tmpfilename = $q->tmpFileName($file_hash{$key}); my $destFile = File::Spec->catfile($upload_folder,$base); copy( $tmpfilename, $destFile ) or die "Copy to ($destFile) failed +: $!\n"; print '<li>Sucessfully uploaded --- <b>', CGI->escapeHTML(basename +($key)), '</b></li>'; } print '</ul>'; print "<p><b>Name</b> -- $name<br><b>Email</b> -- $email<br><b>Comment +s</b> -- $comments<br>"; print $q->end_html;
So far so good. Thanks again for all the help. Will be posting my solution on Stackoverflow as well to close that out.

Replies are listed 'Best First'.
Re: CGI, uploading multiple files
by huck (Prior) on Mar 14, 2017 at 05:07 UTC

    Well i found out what was in the parts, PERLIO filehandles

    and i found the MAGIC SPELL http://stackoverflow.com/questions/3196783/perl-file-upload-cant-init-filehandle File upload forms need to specify enctype="multipart/form-data". ----- Sinan Ünür

    Ubuntu tested code (well at least with $tostdout=1).

    AND Notice how the filename starts with a folder!

    #!/usr/bin/perl use strict; use warnings; use CGI; use Data::Dumper; use HTML::Entities qw/encode_entities/; my $q = CGI->new(); print $q->header; unless ($q->param('multi_files')) { print ' <html> <head> <title>Stupid Test Site</title> </head> <body> <h1>This is a test</h1> <form action="multiupload.pl" enctype="multipart/form-data". +method="POST"> Your Name: <input type="text" name="name"><br> Email Address: <input type="text" name="email"><br> Select PO folder: <input name="multi_files" type="file" we +bkitdirectory multiple /><br/> Comments:<br> <textarea name="comments" rows="5" cols="60"></textarea><b +r> <input type="submit" value="Send"> </form> </body> </html> '; exit; } my $upload_folder = '/var/www/html/uploads'; my $name = $q->param('name'); my $email = $q->param('email'); my $comments = $q->param('comments'); my @files = $q->param('multi_files'); my $tostdout=1; ###### see ###### http://stackoverflow.com/questions/3196783/perl-file-upload-can +t-init-filehandle ###### File upload forms need to specify enctype="multipart/form-data" +. ----- Sinan Ünür my @io_handles=$q->upload('multi_files'); print '<pre>'; print Dumper(\@files); print Dumper(\@io_handles); print '</pre>'; print '<br>step0'; for my $upload (@files){ print "<br>Upload this please -- $upload<br>"; if ($upload){ eval { if ($tostdout) { print '<pre>' ; while ( my $buffer = <$upload>) { print STDOUT encode_entities($buffer); } # while print '</pre>' ; } # tostdout else { open (OUTFILE,">$upload_folder/$upload") or die $!;; binmode OUTFILE; while ( my $buffer = <$upload>) { print OUTFILE $buffer; } # while close OUTFILE; } # notstdout }; # eval print 'error:'.$@.'<br>' if $@; } # upload else { print "<br><b>Guess it's broken</b><br/>"; } # else } # $upload print "<p><b>Name</b> -- $name<br><b>Email</b> -- $email<br><b>Comment +s</b> -- $comments<br>"; print $q->end_html;
    Result
    $VAR1 = [ bless( \*{'Fh::fh00001multiuploads/a.txt'}, 'Fh' ), bless( \*{'Fh::fh00002multiuploads/Copy of a.txt'}, 'Fh' ), bless( \*{'Fh::fh00003multiuploads/Copy (2) of a.txt'}, 'Fh' + ) ]; $VAR1 = [ bless( \*{'Fh::fh00001multiuploads/a.txt'}, 'Fh' ), bless( \*{'Fh::fh00002multiuploads/Copy of a.txt'}, 'Fh' ), bless( \*{'Fh::fh00003multiuploads/Copy (2) of a.txt'}, 'Fh' + ) ]; step0 Upload this please -- multiuploads/a.txt this is a.txt Upload this please -- multiuploads/Copy of a.txt this is a.txt Upload this please -- multiuploads/Copy (2) of a.txt this is a.txt Name -- a Email -- b Comments -- ok

Re: CGI, uploading multiple files
by trippledubs (Deacon) on Mar 13, 2017 at 19:55 UTC
      I am open to any solution at the moment. This will act as a wrapper around a much more complex perl script which will be used internally by a few employees. I'll take a look at mojo.

        Try it without the if block

        # if ($upload_file){ open (OUTFILE,">$upload_folder/$upload") or die $!;; binmode OUTFILE; while (<$upload_file>) { print OUTFILE; } # } # else { # print "<b>Guess it's broken</b><br/>"; # }

        Also, it might help to add this line while developing but take it out when finshed.

        use CGI::Carp 'fatalsToBrowser'; # use only for test/debug
        
        poj
Re: CGI, uploading multiple files
by huck (Prior) on Mar 13, 2017 at 21:55 UTC

    Have you checked the error.log of your server to see if there are any error conditions raised while it ran?

    The only way i can see it not returning the print "<p><b>Name</b> -- $name<br><b>Email</b> -- $email<br><b>Comments</b> -- $comments<br>"; contents is if the cgi process terminated wit an error before reaching that point, like dieing because it cannot open the file.

    To catch what would otherwise be a terminal error you can wrap the code in an eval block like so

    eval { open (OUTFILE,">$upload_folder/$upload") or die $!;; binmode OUTFILE; while (<$upload_file>) { print OUTFILE; } close OUTFILE; }; print 'error:'.$@.'<br>' if $@;
    the point made by pojabout use CGI::Carp 'fatalsToBrowser'; may serve to do the same thing. Or it may identify another error

    ps, what do you think should be in $upload_file? what parm has the same name as of one of the files to be uploaded, not the value, its name.

    consider

    my @files = $q->param('multi_files'); my @io_handles=$q->upload('multi_files'); foreach my $upload(@files){ print "Upload this please -- $upload<br>"; my $upload_file = shift @io_handles; if ($upload_file){ eval { open (OUTFILE,">$upload_folder/$upload") or die $!;; binmode OUTFILE; while ( my $bytesread = $upload_file->read($buffer,1024) ) { print OUTFILE $buffer; } close OUTFILE; }; print 'error:'.$@.'<br>' if $@; } else { print "<b>Guess it's broken</b><br/>"; } }
    see http://search.cpan.org/~leejo/CGI-4.35/lib/CGI.pod#Processing_a_file_upload_field

    you may also be interested in

    my $type = $q->uploadInfo( $upload_file )->{'Content-Type'};
    To determine if you need binmode and should use read, or if you dont want binmode and can use <$upload_file>

Re: CGI, uploading multiple files
by stevieb (Canon) on Mar 13, 2017 at 19:55 UTC

    It is considered polite to notify all sites involved when you cross-post, so that duplicate efforts aren't needlessly performed for those who don't frequent all the sites you've posted to.

Re: CGI, uploading multiple files
by Anonymous Monk on Mar 13, 2017 at 23:23 UTC

      Thanks for all the feed back and suggestions. I am going to try a few these suggestions and will respond with an update