www :: curl - 如何上传(发布)大文件

时间:2012-02-28 15:52:42

标签: perl file-upload curl libcurl

我使用WWW :: Curl上传文件:

use WWW::Curl::Easy 4.14;
use WWW::Curl::Form;

my $url = 'http://example.com/backups/?sid=12313qwed323';
my $params = {
    name => 'upload',
    action => 'keep',
    backup1 => [ '/tmp/backup1.zip' ],   # 1st file for upload
};

my $form = WWW::Curl::Form->new();
foreach my $k (keys %{$params}) {
    if (ref $params->{$k}) {
        $form->formaddfile(@{$params->{$k}}[0], $k, 'multipart/form-data');
    } else {
        $form->formadd($k, $params->{$k});
    }
}

my $curl = WWW::Curl::Easy->new() or die $!; 
$curl->setopt(CURLOPT_HTTPPOST, $form);
$curl->setopt(CURLOPT_URL, $url);

my $body;   
$curl->setopt(CURLOPT_WRITEDATA, \$body);
my $retcode = $curl->perform();
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE); 

这里没什么特别的,这段代码效果很好。

我想上传大文件,我不想预先加载内存中的所有内容。至少这是我听说libcurl正在做的事情。

CURLOPT_READFUNCTION接受返回部分内容的回调。这意味着我不能使用WWW :: Curl :: Form来设置POST参数,但我必须通过此回调返回整个内容。是吗?

我认为代码看起来像这样:

use WWW::Curl::Easy 4.14;

my $url = 'http://example.com/backups/?sid=12313qwed323'
my $params = {
    name => 'upload',
    action => 'keep',
    backup1 => [ '/tmp/backup1.zip' ],   # 1st file for upload
};

my $fields;
foreach my $k (keys %{$params}) {
    $fields .= "$k=".(ref $params->{$k} ? '@'.@{$params->{$k}}[0] : uri_escape_utf8($params->{$k}))."&";
}
chop($fields);

my $curl = WWW::Curl::Easy->new() or die $!;
$curl->setopt(CURLOPT_POST, 1);
$curl->setopt(CURLOPT_POSTFIELDS, $fields); # is it needed with READFUNCTION??
$curl->setopt(CURLOPT_URL, $url);

my @header = ('Content-type: multipart/form-data', 'Transfer-Encoding: chunked');
$curl->setopt(CURLOPT_HTTPHEADER, \@header);

#$curl->setopt(CURLOPT_INFILESIZE, $size);
$curl->setopt(CURLOPT_READFUNCTION, sub {

    # which data to return here?
    # $params (without file) + file content?

    return 0;
});

CURLOPT_READFUNCTION回调必须返回哪些数据? $ params +文件内容?采用哪种格式?

我是否真的必须自己创建数据(由CURLOPT_READFUNCTION返回),还是有一种简单的方法以正确的格式创建它?

由于

2 个答案:

答案 0 :(得分:4)

测试16formpost.t是相关的。如您所见,它已完全禁用。这个事实以及我对回调函数的各种返回值的无结果实验让我相信Perl绑定中已知的CURLOPT_READFUNCTION特性已被破坏。

  

我必须通过此回调返回整个内容。是吗?

不,你可以分段提供请求体,适合分块编码。根据{{​​1}}中设置的限制,回调将被多次调用。

  

CURLOPT_READFUNCTION回调必须返回哪些数据?

HTTP请求正文。由于您进行文件上传,这意味着Content-Type multipart/form-data。以下是使用HTTP :: Message的示例。 CURLOPT_HTTPPOST是另一种构建此格式的方法。

CURLOPT_INFILESIZE

答案 1 :(得分:3)

CURLOPT_READFUNCTION回调仅用于分块传输编码。它可能工作,但我无法得到它,并且发现无论如何都不需要这样做。

我的用例是将数据上传到AWS,在那里将数据作为多部分表单数据上传是不合适的。相反,它是数据的直接POST。但它确实需要您知道您要向服务器发送多少数据。这似乎对我有用:

my $infile = 'file-to-upload.json';
my $size = -s $infile;
open( IN, $infile ) or die("Cannot open file - $infile. $! \n");

my $curl = WWW::Curl::Easy->new;
$curl->setopt(CURLOPT_HEADER,       1);
$curl->setopt(CURLOPT_NOPROGRESS,   1);
$curl->setopt(CURLOPT_POST,         1);
$curl->setopt(CURLOPT_URL,          $myPostUrl);
$curl->setopt(CURLOPT_HTTPHEADER,   
    ['Content-Type: application/json']); #For my use case
$curl->setopt(CURLOPT_POSTFIELDSIZE_LARGE, $size);
$curl->setopt(CURLOPT_READDATA, \*IN);

my $retcode = $curl->perform;

if ($retcode == 0) {
    print("File upload success\n");
} 
else {
    print("An error happened: $retcode ".$curl->strerror($retcode)."\n");
}

关键是提供对CURLOPT_READDATA的打开文件句柄引用。之后,核心卷曲库处理来自它的读取,而不需要回调。