Search code examples
perlcgi

Multiple File Uploads with Perl CGI


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>Comments</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/jquery.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" webkitdirectory multiple /><br/>
            Comments:<br>
            <textarea name="comments" rows="5" cols="60"></textarea><br>
            <input type="submit" value="Send">
        </form>
    </body>
</html>

I've also posted this question on perlmonks.com as well.


Solution

  • The main issue with my code was it was missing the

    enctype="multipart/form-data"

    from the form. I made some changes as well. Here is my 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 directory: $!\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>Comments</b> -- $comments<br>";
    
    print $q->end_html;
    

    Thanks for the help. (I posted this answer on perlmonks.com as well)