You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Rich Bowen <rb...@CooperMcGregor.com> on 2002/03/12 12:57:26 UTC

File upload example

I am sure that this is a FAQ, but I had a very hard time finding
examples of code for doing file upload. I wanted to post this here in
order to have it in the permanent record so that other folks don't have
to spend days figuring this out.

I have a generic form method which looks like:


sub form {
    use Apache::Request;
    my $r = Apache->request();
    my $apr = Apache::Request->new($r);
    my @keys = $apr->param;

    my %form;
    foreach my $key(@keys) {

        my @value = $apr->param($key);
        next unless scalar @value;

        if ( @value > 1 ) {
            $form{$key} = \@value;
          } else {
            $form{$key} = $value[0];
        }
    }

    my $upload = $apr->upload;
    if ($upload) {
        $form{UPLOAD} = $upload;
    }

   return \%form;
}

And the explanation is thus. This function returns a hashref. The keys
of the hash are the names in your form. The values in the hash are the
values entered in those fields, with the exception that a multiple
select list with multiple things selected will return a listref. This is
the way that CGI_Lite, for example, handles things, and that is what I
was trying to be compatible with for my own legacy code. You can of
course change that if you have different conventions.

*If* your form contained a file upload element, (Don't forget to make
your form encoding="multiplar/form-data"  I always forget that.)
then $form{UPLOAD} will contain a file upload object, which you can
make calls back into.

For example:

my $form = Your::Class::form(); # Wherever you put this function
if (my $file = $form->{UPLOAD}) {
    my $filename = $file->filename; # If you need the name

    # And, if you want to save the file at $filelocation ...
    open F, ">$filelocation";
    my $filehandle = $file->fh;
    while (my $d = <$filehandle>) {
        print F $d;
    }
    close F;
}

That should give you the general idea of how this works. This lets you
have a generic form handler that does "normal" forms as well as file
upload forms, in mod_perl, without having to mess with CGI.pm (which I
saw a lot of people doing, and did not really understand very well) and
without having to do custom things when you have a file upload.

Comments welcome, YMMV, Caveat Emptor, and all that.

-- 
Rich Bowen
Apache Administrators Handbook
ApacheAdmin.com


Re: File upload example

Posted by Rich Bowen <rb...@CooperMcGregor.com>.
On Wed, 13 Mar 2002, John Saylor wrote:

> Hi
>
> > On Tue, 12 Mar 2002, John Saylor wrote:
> > > I have found that some browsers put the file in the value matching
> > > the parameter name instead of putting a file upload object there.
>
> ( 02.03.12 18:36 -0500 ) Rich Bowen:
> > That's not really necessary, as Apache::Request does that for you.
>
> Maybe I was using an earlier version, but Apache::Request was NOT doing
> it for me, so that is why I needed to do this extra stuff.

OK. Nasty. I will check into this more carefully.

-- 
Rich Bowen
http://www.apacheadmin.com/
Apache Support and Training


Re: File upload example

Posted by John Saylor <jo...@worldwinner.com>.
Hi

> On Tue, 12 Mar 2002, John Saylor wrote:
> > I have found that some browsers put the file in the value matching
> > the parameter name instead of putting a file upload object there.

( 02.03.12 18:36 -0500 ) Rich Bowen:
> That's not really necessary, as Apache::Request does that for you.

Maybe I was using an earlier version, but Apache::Request was NOT doing
it for me, so that is why I needed to do this extra stuff.

-- 
\js	"embrace impactful experiences"

Re: File upload example

Posted by Rich Bowen <rb...@CooperMcGregor.com>.
On Tue, 12 Mar 2002, John Saylor wrote:

> Hi
>
> ( 02.03.12 06:57 -0500 ) Rich Bowen:
> > Comments welcome, YMMV, Caveat Emptor, and all that.
>
> I have found that some browsers put the file in the value matching the
> parameter name instead of putting a file upload object there. So your
> code should check the value to see if it is a path AND a one liner
> BEFORE trying to create the file upload object.

That's not really necessary, as Apache::Request does that for you. If
the upload method fails, then you won't get anything in the UPLOAD key.
The generic form handler does not know what field(s) in your form were
file upload forms, and so this method just lets you check the one key
(UPLOAD) and, if it is defined, then you know you got something.

Hopefully, *all* browsers put the file name in the parameter, since that
is the defined behavior. However, regardless of this, the file upload
object contains all the necessary information to reconstruct the file,
so you don't even have to look in that field.

-- 
Rich Bowen
Apache Administrators Handbook
ApacheAdmin.com


Re: File upload example

Posted by John Saylor <jo...@worldwinner.com>.
Hi

( 02.03.12 06:57 -0500 ) Rich Bowen:
> Comments welcome, YMMV, Caveat Emptor, and all that.

I have found that some browsers put the file in the value matching the
parameter name instead of putting a file upload object there. So your
code should check the value to see if it is a path AND a one liner
BEFORE trying to create the file upload object.

-- 
\js	"extend scalable infomediaries"

Re: File upload example

Posted by Rich Bowen <rb...@CooperMcGregor.com>.
On Tue, 12 Mar 2002, Stas Bekman wrote:

> Rich Bowen wrote:
> > I am sure that this is a FAQ, but I had a very hard time finding
> > examples of code for doing file upload. I wanted to post this here in
> > order to have it in the permanent record so that other folks don't have
> > to spend days figuring this out.
>
> Great Rich! I think we can do better than just keeping it in the
> archive. How about adding it here?
> http://perl.apache.org/guide/snippets.html
> If you like the idea, can you please make it a complete section and send
> it to list/me and I'll add it to the guide? Thanks!

Absolutely. I will try to do that later this week.

-- 
http://www.CooperMcGregor.com/
Apache Support and Training


Re: File upload example

Posted by Stas Bekman <st...@stason.org>.
Rich Bowen wrote:
> I am sure that this is a FAQ, but I had a very hard time finding
> examples of code for doing file upload. I wanted to post this here in
> order to have it in the permanent record so that other folks don't have
> to spend days figuring this out.

Great Rich! I think we can do better than just keeping it in the 
archive. How about adding it here?
http://perl.apache.org/guide/snippets.html
If you like the idea, can you please make it a complete section and send 
it to list/me and I'll add it to the guide? Thanks!



_____________________________________________________________________
Stas Bekman             JAm_pH      --   Just Another mod_perl Hacker
http://stason.org/      mod_perl Guide   http://perl.apache.org/guide
mailto:stas@stason.org  http://ticketmaster.com http://apacheweek.com
http://singlesheaven.com http://perl.apache.org http://perlmonth.com/


Re: File upload example

Posted by "D. Hageman" <dh...@dracken.com>.
Mozilla sends a fully qualified path name ... just an FYI ...

On Tue, 12 Mar 2002, Robert Landrum wrote:

> At 9:01 AM -0800 3/12/02, David Wheeler wrote:
> >On Tue, 2002-03-12 at 03:57, Rich Bowen wrote:
> >
> >> my $form = Your::Class::form(); # Wherever you put this function
> >> if (my $file = $form->{UPLOAD}) {
> >>     my $filename = $file->filename; # If you need the name
> >
> >Actually, if you want the name, it's a really good idea to just get the
> >basename, since some browsers on some platforms (e.g., IE/Mac) send the
> >complete path name to the file on the browser's local file system (e.g.,
> >':Mac:Foo:Bar:image.jpg'). This is trickier than it sounds, because you
> >have to tell basename() what platform to assume the file is from. Here's
> >how I suggest doing it.
> 
> Since when?  I just wrote something that did just this (in CGI), but 
> it only uploaded the basename.  I'm using Mac OS 9, IE 5.0.  That 
> sounds a lot like IE 3.0.
> 
> The other way to go is
> 
> $filename = $1 if($filename =~ /[\:\/\\]([^\:\/\\]+)$/);
> 
> How many people use / : or \ in their paths?  Can we shoot those 
> people?  I especially don't want people who use those characters 
> uploading files that might be downloaded by someone on another 
> platform.  Just think what would happen if I downloaded Foo:Bar.txt, 
> as uploaded by my windows friends.
> 
> Rob
> 
> --
> When I used a Mac, they laughed because I had no command prompt. When 
> I used Linux, they laughed because I had no GUI.  
> 

-- 
//========================================================\\
||  D. Hageman                    <dh...@dracken.com>  ||
\\========================================================//


Re: File upload example

Posted by Robert Landrum <rl...@capitoladvantage.com>.
At 9:01 AM -0800 3/12/02, David Wheeler wrote:
>On Tue, 2002-03-12 at 03:57, Rich Bowen wrote:
>
>> my $form = Your::Class::form(); # Wherever you put this function
>> if (my $file = $form->{UPLOAD}) {
>>     my $filename = $file->filename; # If you need the name
>
>Actually, if you want the name, it's a really good idea to just get the
>basename, since some browsers on some platforms (e.g., IE/Mac) send the
>complete path name to the file on the browser's local file system (e.g.,
>':Mac:Foo:Bar:image.jpg'). This is trickier than it sounds, because you
>have to tell basename() what platform to assume the file is from. Here's
>how I suggest doing it.

Since when?  I just wrote something that did just this (in CGI), but 
it only uploaded the basename.  I'm using Mac OS 9, IE 5.0.  That 
sounds a lot like IE 3.0.

The other way to go is

$filename = $1 if($filename =~ /[\:\/\\]([^\:\/\\]+)$/);

How many people use / : or \ in their paths?  Can we shoot those 
people?  I especially don't want people who use those characters 
uploading files that might be downloaded by someone on another 
platform.  Just think what would happen if I downloaded Foo:Bar.txt, 
as uploaded by my windows friends.

Rob

--
When I used a Mac, they laughed because I had no command prompt. When 
I used Linux, they laughed because I had no GUI.  

Re: File upload example

Posted by David Wheeler <da...@wheeler.net>.
On Tue, 2002-03-12 at 03:57, Rich Bowen wrote:

> my $form = Your::Class::form(); # Wherever you put this function
> if (my $file = $form->{UPLOAD}) {
>     my $filename = $file->filename; # If you need the name

Actually, if you want the name, it's a really good idea to just get the
basename, since some browsers on some platforms (e.g., IE/Mac) send the
complete path name to the file on the browser's local file system (e.g.,
':Mac:Foo:Bar:image.jpg'). This is trickier than it sounds, because you
have to tell basename() what platform to assume the file is from. Here's
how I suggest doing it.

    use File::Basename;
    use HTTP::BrowserDetect;

    my $browser = HTTP::BrowserDetect->new($ENV{HTTP_USER_AGENT});

    # Tell File::Basename what platform to expect.
    fileparse_set_fstype($browser->mac ? 'MacOS' :
                         $browser->windows ? 'MSWin32' :
                         $browser->dos ? 'MSDOS' :
                         $browser->vms ? 'VMS'
                         $browser->amiga ? 'AmigaOS' :
                         $^O);

    # Get the file name.
    my $filename = basename($file->filename);

    # Be sure to set File::Basename to the local file system again.
    fileparse_set_fstype($^O);

HTH,

David

-- 
David Wheeler                                     AIM: dwTheory
david@wheeler.net                                 ICQ: 15726394
http://david.wheeler.net/                      Yahoo!: dew7e
                                               Jabber: Theory@jabber.org