我一直在努力将内部应用程序转换为远离使用FTP,因为安全团队告诉我们要离开FTP。所以我一直在使用HTTP上传,而且大多数情况下它运行得很好。我们的环境是Linux,HP-UX,Solaris和AIX的混合体。在我们的Linux服务器上,curl是普遍可用的,所以我一直在使用curl的POST功能进行上传,并且它运行得很完美。不幸的是,Unix机器很少有卷曲,甚至wget,所以我用perl编写了一个GET脚本,工作正常,我为perl编写的POST脚本(从网上其他地方解除并改编)对Unix来说非常出色,直到上传的数据大于约60K(在Linux中卷曲处理正常,顺便说一句)。除此之外,Apache错误日志开始吐出:
CGI.pm: Server closed socket during multipart read (client aborted?).
当我使用curl进行上传时,不会发生此类错误。这是我的POST脚本,使用Socket,因为LWP并非在每台服务器上都可用,而且在任何Unix服务器上都没有。
#!/usr/bin/perl -w
use strict;
use Socket;
my $v = 'dcsm';
my $upfile = $ARGV[0] or die 'Upload File not found or not specified.' . "\n";
my $hostname = $ARGV[1] or die 'Hostname not specified.' . "\n";
$| = 1;
my $host = "url.mycompany dot com";
my $url = "/csmtar.cgi";
my $start = times;
my ( $iaddr, $paddr, $proto );
$iaddr = inet_aton($host);
$paddr = sockaddr_in( 80, $iaddr );
$proto = getprotobyname('tcp');
unless ( socket( SOCK, PF_INET, SOCK_STREAM, $proto ) ) {
die "ERROR : init socket: $!";
}
unless ( connect( SOCK, $paddr ) ) {
die "no connect: $!\n";
}
my $length = 0;
open( UH, "< $upfile" ) or warn "$!\n";
$length += -s $upfile;
my $boundary = 'nn7h23ffh47v98';
my @head = (
"POST $url HTTP/1.1",
"Host: $host",
"User-Agent: z-uploader",
"Content-Length: $length",
"Content-Type: multipart/form-data; boundary=$boundary",
"",
"--$boundary",
"Content-Disposition: form-data; name=\"hostname\"",
"",
"$hostname",
"--$boundary",
"Content-Disposition: form-data; name=\"ren\"",
"",
"true",
"--$boundary",
"Content-Disposition: file; name=\"filename\"; filename=\"$upfile\"",
"--$boundary--",
"",
"",
);
my $header = join( "\r\n", @head );
$length += length($header);
$head[3] = "Content-Length: $length";
$header = join( "\r\n", @head );
$length = -s $upfile;
$length += length($header);
select SOCK;
$| = 1;
print SOCK $header;
while ( sysread( UH, my $buf, 8196 ) ) {
if ( length($buf) < 8196 ) {
$buf = $buf . "\r\n--$boundary";
syswrite SOCK, $buf, length($buf);
} else {
syswrite SOCK, $buf, 8196;
}
print STDOUT '.',;
}
close UH;
shutdown SOCK, 1;
my @data = (<SOCK>);
print STDOUT "result->@data\n";
close SOCK;
有人看到突然出现的东西吗?
更新
我做了以下更新,错误似乎没有改变。
为了解决内容长度问题,并尝试在追加最终边界之前消除循环等于确切字符数的可能性,我做了以下代码更新。
my $boundary = 'nn7h23ffh47v98';
my $content = <<EOF;
--$boundary
Content-Disposition: form-data; name="hostname"
$hostname
--$boundary
Content-Disposition: file; name="filename"; filename="$upfile"
--$boundary--
EOF
$length += length($content);
my $header = <<EOF;
POST $url HTTP/1.1
Host: $host
User-Agent: z-uploader
Content-Length: $length
Content-Type: multipart/form-data; boundary=$boundary
EOF
$header .= $content;
select SOCK;
$| = 1;
print SOCK $header;
my $incr = ($length + 100) / 20;
$incr = sprintf("%.0f", $incr);
while (sysread(UH, my $buf, $incr )) {
syswrite SOCK, $buf, $incr;
}
syswrite SOCK, "\n--$boundary", $incr;
答案 0 :(得分:1)
通过查看代码,你会问是否有“跳出来的东西”。
有两件事突然袭来我:
1)POST HTTP消息中的Content-Length参数指定HTTP消息的实体部分的确切字节数。请参阅RFC 2616的第4.4节。
您正在将Content-Length:标头设置为您要上传的文件的确切大小。不幸的是,除了文件本身,您还发送了MIME标题。
HTTP消息的“实体”部分,如RFC 2616所定义,基本上由HTTP消息头的最后一个头后面的空白行之后的所有内容组成。该点下方的 Everything 必须包含在Content-Length:
标题中。 Content-Length
标头不是您要上传的文件的大小,而是与标头后面的HTTP消息的整个实体部分相同。
2)忽略损坏的Content-Length:
标头,如果文件的大小恰好是8196字节的整数倍,则您构建的MIME文档很可能已损坏。你的上一次sysread()调用将获取文件中的最后8196个字节,您将很乐意复制,下一次调用sysread()将返回0,并且您将终止循环,而不会发出尾部边界分隔符。 MIME文档将损坏。