LWPでできるかなって思ってたら、すぐできなかったのでSocket叩いて書いたけど、
調べたら普通にLWPで出来た。ですよねー。
use LWP::UserAgent; use HTTP::Request::Common; my $ua = new LWP::UserAgent; my $res=$ua->request( POST('http://192.168.11.254/upload', Content_Type => 'multipart/form-data', Content => [ 'file' => ['test.txt', '/tmp/test.txt'] ] ) );
出力例
POST /upload HTTP/1.1 TE: deflate,gzip;q=0.3 Connection: TE, close Host: 192.168.11.254 User-Agent: libwww-perl/5.837 Content-Length: 134 Content-Type: multipart/form-data; boundary=xYzZY --xYzZY Content-Disposition: form-data; name="file"; filename="/tmp/test.txt" Content-Type: text/plain Hello World --xYzZY--
パラメータ名'file'に'test.txt'の内容を吐き出すけど、
filenameは'/tmp/test.txt'とするようになる。
filenameの値を信じてる手抜きアップローダならごにょごにょ。
お蔵入りしたもの。multipart/form-dataを自力で書き出すだけ。
use strict; use warnings; use IO::Socket::INET; run(); sub run{ my $ip = '127.0.0.1'; my $port = '3000'; my $path = '/upload'; my $boundary = '---------------------------9930139772306'; my $name = 'file'; my $filename = '../test.txt'; my $type = 'application/octet-stream'; my $sock = IO::Socket::INET->new(PeerAddr => $ip, PeerPort => $port, Proto => 'tcp'); open(my $FH1, '<', 'test.txt') || die; open(my $FH2, '<', 'sample.mp4') || (close($FH1), die); sendMultipartFormData($sock, { host => 'rying.net', path => $path, boundary => $boundary, autoclose => 1, debugoutput => *STDOUT, sendbuffersize => 1024 * 64, data => [ { name => 'file', filename => 'output.test.txt', type => $type, body => $FH1, encoding => 'binary', }, { name => 'file', filename => 'output.sample.mp4', type => $type, body => $FH2, encoding => 'binary', }, { name => 'mode', body => 'up', } ] }); print while <$sock>; $sock->close(); } sub sendMultipartFormData{ my $sock = shift || die; my $args = shift || die; # hash ref my $host = defined($args->{host}) ? delete($args->{host}) : die; my $path = defined($args->{path}) ? delete($args->{path}) : die; my $originalboundary = defined($args->{boundary}) ? delete($args->{boundary}) : die; my $autoclose = delete($args->{autoclose}); my $debugoutput = delete($args->{debugoutput}); my $sendbuffersize = delete($args->{sendbuffersize}); my $datalist = defined($args->{data}) ? delete($args->{data}) : die; if(ref $datalist eq 'HASH'){ $datalist = [$datalist]; }elsif(ref $datalist ne 'ARRAY'){ die; } # boundary my $boundary = "\r\n--" . $originalboundary . "\r\n"; my $lastboundary = "\r\n--" . $originalboundary . "--\r\n"; # 総ファイルサイズ算出とデータの確認 my $totalheadersize = 0; my $totalfilesize = 0; foreach my $data(@$datalist){ use bytes; if(!exists($data->{name}) || !exists($data->{body})){ die; } my $name = $data->{name}; my $filename = $data->{filename}; my $type = $data->{type}; my $encoding = $data->{encoding}; my $body = $data->{body}; # text or FileHandle my $bodysize = $data->{bodysize}; if(not defined $bodysize){ # FileHandle if(ref $body eq 'GLOB' || ref eq 'IO::File'){ seek($body, 0, 2); $bodysize = tell($body); seek($body, 0, 0); }elsif(ref $body eq ''){ $bodysize = bytes::length($body); }else{ die; } $data->{bodysize} = $bodysize; } $totalfilesize += $bodysize; $totalheadersize += bytes::length($boundary); # boundary $totalheadersize += bytes::length($name) + 40; # "Content-Disposition: form-data; name="$name";" $totalheadersize += 12 + bytes::length($filename) if $filename; # " filename="$filename"" $totalheadersize += 2; # "\r\n"(Content-Disposition) $totalheadersize += 14 + bytes::length($type) + 2 if $type; # "Content-Type: $type\r\n" $totalheadersize += 27 + bytes::length($encoding) + 2 if $encoding; # "Content-Transfer-Encoding: $encoding\r\n" $totalheadersize += 2; # "\r\n" } use bytes; my $contentlength = $totalheadersize - 2 + $totalfilesize + bytes::length($lastboundary); # ヘッダとボディの境界の"\r\n"は含めないため。 no bytes; # データ送信 binmode $sock || die; $sock->autoflush(1); my $header = "POST $path HTTP/1.1\r\n". "Connection: close\r\n". "Host: $host\r\n". "Content-Type: multipart/form-data; boundary=$originalboundary\r\n". "Content-Length: $contentlength\r\n"; print $sock $header; print $debugoutput $header if($debugoutput); foreach my $data(@$datalist){ my $name = $data->{name}; my $filename = $data->{filename}; my $body = $data->{body}; # text or FileHandle my $type = $data->{type}; my $encoding = $data->{encoding}; my $contentdisposition = qq{Content-Disposition: form-data; name="$name";}; if($filename){ $contentdisposition .= qq{ filename="$filename"}; } $contentdisposition .="\r\n"; my $contenttype = $type ? "Content-Type: $type\r\n" : ""; my $contenttransferencoding = $encoding ? "Content-Transfer-Encoding: $encoding\r\n" : ""; print $sock $boundary; print $sock $contentdisposition; print $sock $contenttype; print $sock $contenttransferencoding; print $sock "\r\n"; if($debugoutput){ print $debugoutput $boundary; print $debugoutput $contentdisposition; print $debugoutput $contenttype; print $debugoutput $contenttransferencoding; print $debugoutput "\r\n"; } if(ref $body){ my $buf; if($encoding){ binmode $body || die; } print $debugoutput "[$filename]" if($debugoutput); while(read($body, $buf, $sendbuffersize)){ print $sock $buf; } if($autoclose){ close $body; } }else{ print $sock $body; print $debugoutput $body if($debugoutput); } $sock->flush(); } print $debugoutput $lastboundary if($debugoutput); print $sock $lastboundary; }