文件大小超过60K时文件上传失败

时间:2014-12-15 01:00:22

标签: linux perl http unix command-line-interface

我一直在努力将内部应用程序转换为远离使用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;

1 个答案:

答案 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文档将损坏。