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

Hello
I'm writing a small Perl script (http client) that only uploads files to a webserver. I do not want to use FTP. I want to use HTTP and a multipart POST.

I think I can accomplish this by using IO::Socket, creating a multipart MIME format, and stepping through the lines in the file one by one (writing to the socket). However, I do not know the protocol rules for doing this. I can send regular POSTs, but when i send the multipart POST and try to read the server response it just hangs (waiting for response). When I look at the server side nothing is uploaded. Am i missing something?

# Read file: open(FILE, $file); # Convert to multipart MIME & save in temp file # Make socket connection: $socket = IO::Socket.... # Generate HTTP POST HEADER: print $socket "POST /cgi-bin/upload.pl HT +TP/1.0\n" etc. # Read multipart temp file line by line and write to socket: while(<TE +MP>){ print $socket } # When finished (EOF): print "\n\r\n\r"; # Read server response: while(<$socket>){ print }

Replies are listed 'Best First'.
Re: uploading a file through HTTP using multipart
by chromatic (Archbishop) on Nov 22, 2000 at 00:27 UTC
    If you are determined to do this on your own, eventually you will need to look at the HTTP standard as defined in the relevant RFCs: RFC1945 and RFC2616.

    From the information you have given, I would suggest you add an extra newline to the POST header. Also, you will need to send a 'Content-type: multipart/form-data' header and probably need to add some really ugly boundary strings. Yuck.

    If I were to do this, I would rush to the LWP-Bundle module, as it handles all of this for you already. See perldoc lwpcook and search for POST.

Re: uploading a file through HTTP using multipart
by merlyn (Sage) on Nov 22, 2000 at 00:36 UTC
    As chromatic said, the simplest way to do this is with LWP. You want HTTP::Request::Common, from which the doc reads:
    POST $url, [$form_ref], [Header => Value,...] This works mostly like GET() with POST as the method, but this function also takes a second optional array or hash reference parameter ($form_ref). This argu- ment can be used to pass key/value pairs for the form content. By default we will initialize a request using the `application/x-www-form-urlencoded' content type. This means that you can emulate a HTML <form> POSTing like this: POST 'http://www.perl.org/survey.cgi', [ name => 'Gisle Aas', email => 'gisle@aas.no', gender => 'M', born => '1964', perc => '3%', ]; This will create a HTTP::Request object that looks like this: POST http://www.perl.org/survey.cgi Content-Length: 66 Content-Type: application/x-www-form-urlencoded name=Gisle%20Aas&email=gisle%40aas.no&gender=M&born=1964& +perc=3%25 The POST method also supports the `multi- part/form-data' content used for Form-based File Upload as specified in RFC 1867. You trigger this content format by specifying a content type of `'form-data'' as one of the request headers. If one of the values in the $form_ref is an array reference, then it is treated as a file part specification with the following interpretation: [ $file, $filename, Header => Value... ] The first value in the array ($file) is the name of a file to open. This file will be read and its content placed in the request. The routine will croak if the file can't be opened. Use an `undef' as $file value if you want to specify the content directly. The $filename is the filename to report in the request. If this value is undefined, then the basename of the $file will be used. You can specify an empty string as $filename if you don't want any filename in the request. Sending my ~/.profile to the survey used as example above can be achieved by this: POST 'http://www.perl.org/survey.cgi', Content_Type => 'form-data', Content => [ name => 'Gisle Aas', email => 'gisle@aas.no', gender => 'M', born => '1964', init => ["$ENV{HOME}/.profile"], ] This will create a HTTP::Request object that almost looks this (the boundary and the content of your ~/.profile is likely to be different): POST http://www.perl.org/survey.cgi Content-Length: 388 Content-Type: multipart/form-data; boundary="6G+f" --6G+f Content-Disposition: form-data; name="name" Gisle Aas --6G+f Content-Disposition: form-data; name="email" gisle@aas.no --6G+f Content-Disposition: form-data; name="gender" M --6G+f Content-Disposition: form-data; name="born" 1964 --6G+f Content-Disposition: form-data; name="init"; filename=".p +rofile" Content-Type: text/plain PATH=/local/perl/bin:$PATH export PATH --6G+f--
    No point in reinventing the wheel, when it's all nicely there for the taking.

    -- Randal L. Schwartz, Perl hacker

      Thanks,

      However, I am having a similar problem to the one described in node 32916. I have the Multipart MIME constructed, but I cannot POST it correctly. When I do a post and then wait for the server response it hangs indefinitely. What is wrong? I think I am very close to getting it to work, I just need to terminate the multipart or something...

      I do not want to rewrite the thing just so I can use some module.

      Thank You.

Re: uploading a file through HTTP using multipart
by amelinda (Friar) on Nov 23, 2000 at 05:45 UTC
    If I could give you only one piece of advice, it would be this: Use LWP

    Of course, if you don't want to do that (or can't for some convoluted reason), you might want to take a look at the insanity I once had to deal with. Be warned, the RFC's for MIME are awful and difficult to read.

Re: uploading a file through HTTP using multipart
by strredwolf (Chaplain) on Nov 22, 2000 at 07:51 UTC
Re: uploading a file through HTTP using multipart
by tune (Curate) on Nov 22, 2000 at 12:52 UTC
    The CGI module is also capable to handle your problem. It was very useful for me, however I did not want to trick with it. Just upload the file and save to a temporary place, and deny if it was too big. Search the string 'upload' in the manual of CGI.pm! :-)

    -- tune