http://qs321.pair.com?node_id=143017

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

oh people so wiser than I,
I am creating an online photo album using Image Magick's perl library for some neat additional functions. first of all I'm going to plug Image Magick's perl module. any monkey can figure it out and it has plenty of features. anyway, the problem is not with Imagick, it's with CGI.pm's upload function. here's some abridged code from both the html and the cgi script that comes next:
<form action="upload.cgi" enctype="multipart/form-data" method="POST"> <input type="file" name="file"> <input type="submit"> </form>
and then upload.cgi
#!/usr/bin/perl use CGI; $file = upload('file'); open(TEMP,">temp.jpg"); print TEMP $file; close(TEMP);
the resulting temp.jpg is NOT the image I uploaded, but rather the path and filename it was uploaded from. I'm baffled as to why. I know the image is sent to the server- I can watch the 20KB tick off in my dial-up status window. I don't know what everyone else thinks, but it seems real weird to me. One would think that for security reasons the local path/filename wouldn't even be allowed to be sent to a server, but it sure gets there and it sure fills $file. Thanks in advance for any help on this one. -Eric Steinberg

Replies are listed 'Best First'.
Re: CGI.pm's upload gives local filename, not file itself
by wog (Curate) on Feb 03, 2002 at 04:35 UTC
    The upload() function's return is meant to be used as a filehandle (a re-writing of your code):

    #!/usr/bin/perl -w use strict; use CGI qw(:standard); # (update: in your code you didn't import anything?! # I guess that you didn't show us the real code then... # (copy-and-paste if your friend.) (I'm referring # to the 'use CGI' line, of course.)) my $fh = upload('file'); open TEMP, ">temp.jpg" or die "error opening temp.jpg for writing: $!\ +n"; binmode TEMP; # very important on Windows-based machines { # this block is so the assignment to $/ # will not effect the rest of the script local $/ = \1024; # tell <...> to read in 1024-byte chunks. print TEMP $_ while <$fh>; } close TEMP or warn "error closing temp.jpg: $!\n";

    (The use of strict and warnings is strongly reccommended.)

      indeed that's not the original code, that was the code I simplified it down to thinking I had made some stupid mistake. your additions seemed to do the trick. thanks!

      -Eric
        ++wog for getting you on the right track and ++tilly for explaining where and why the problem was arising. I just thought that I would add this as an extra. To help show what you can do with the "local" filename and why you might want it. Also, if you are using CGI.pm, you might aswell use it.
        #!/usr/bin/perl use CGI; $co = new CGI; if (!$co->param()) { print $co->header, $co->start_html('upload a file'), "<table border=1><tr><td>", $co->center('upload a file'), $co->start_multipart_form, $co->filefield(-name=>'file1', -size=>30), $co->br, $co->submit(-value=>'Upload'), $co->end_form, "</td></tr></table>"; } else { $file = $co->param('file1'); print $co->header, $co->start_html('file uploaded'), "Uploading $file ...<br>"; @filename = split(/\\/, $file); open (FILE, ">safe/$filename[$#filename]"), print "... $filename[$#filename] uploaded."; print FILE <$file>; close FILE; } print $co->end_html;
        This is working, but of course, this script is JUST AN EXAMPLE, and you should not trust input forms. Use -T and check the file path for bad chars. Basic error checking, referrer checking, etc.
        "Better secure than sorry." -xtype
Re (tilly) 1: CGI.pm's upload gives local filename, not file itself
by tilly (Archbishop) on Feb 03, 2002 at 14:57 UTC
    You have already been told how to get the result you want, but not why it was done this way.

    The thought was that the browser sends the actual filename of what is sent, and that information can be useful, so it should not be lost. However that name can have any garbage, and so should not be trusted. Furthermore the name of the temporary file that has the data should be irrelevant to you, you just want to get the data and do something with it.

    Therefore the API was chosen so that you can get all of the information that is relevant to you and the temporary file is managed without any work on your part. (I also think that it tends to confuse people to have this magical thing which can be used as a string or a filehandle both, but that is how it was done.)

Re: CGI.pm's upload gives local filename, not file itself
by jonjacobmoon (Pilgrim) on Feb 03, 2002 at 04:35 UTC
Re: CGI.pm's upload gives local filename, not file itself
by giulienk (Curate) on Feb 03, 2002 at 13:37 UTC
    The behavior of sending the entire filepath to the server is peculiar to Internet Explorer. So you should filter the filename with a regex to have a valid filename. Something like the following could do:
    my ($new_file_name) = $full_path_name =~ m!(?:.*\\|.*\/)?(.+)$!;
    Update: well, it seems your problem was of another nature and already been solved: it's a bad habit to post before reading the full thread... sorry
    $|=$_='1g2i1u1l2i4e2n0k',map{print"\7",chop;select$,,$,,$,,$_/7}m{..}g
      Or you could use File::Basename which I believe is standard with the Perl distributions.
      use File::Basename; my $new_file_name = basename($full_path_name, 'jpg|JPG'); # or $new_file_name = basename($full_path_name, '\..*'); # but this will return "James" from # "James.John.jpg"
      or you could use split, or many other options. :)

      jarich

        Using File::Basename isn't that easy indeed, cause you got to suggest him the operating system of the client.
        You can do it by checking CGI server variables but i prefer the regex way, it's more straight forward IMHO.
        Maybe split solution could be more readable, even if less efficient:
        my $new_file_name = pop @{[split /\/|\\/, $full_path_name]};
        $|=$_='1g2i1u1l2i4e2n0k',map{print"\7",chop;select$,,$,,$,,$_/7}m{..}g