www :: curl - как загружать (публиковать) большие файлы - PullRequest
5 голосов
/ 28 февраля 2012

Я использую 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 ]

4 голосов
/ 28 февраля 2012

Тест 16formpost.t имеет значение. Как видите, он полностью отключен. Этот факт и мои бесплодные эксперименты с различными возвращаемыми значениями для функции обратного вызова позволяют мне полагать, что функция CURLOPT_READFUNCTION, как известно, нарушена в привязке Perl.

Я должен вернуть весь контент через этот обратный вызов. Это верно?

Нет, вы можете передать тело запроса по частям, пригодное для кодирования по частям. Обратный вызов обязательно будет вызван несколько раз, в соответствии с лимитом, установленным в CURLOPT_INFILESIZE.

Какие данные должен возвращать обратный вызов CURLOPT_READFUNCTION?

Тело HTTP-запроса. Поскольку вы выполняете загрузку файла, это означает Content-Type multipart / form-data . Ниже приведен пример использования HTTP :: Message. CURLOPT_HTTPPOST - еще один способ создания этого формата.

use HTTP::Request::Common qw(POST);
use WWW::Curl::Easy 4.14;

my $curl = WWW::Curl::Easy->new or die $!;
$curl->setopt(CURLOPT_POST, 1);
$curl->setopt(CURLOPT_URL, 'http://localhost:5000');
$curl->setopt(CURLOPT_HTTPHEADER, [
    'Content-type: multipart/form-data', 'Transfer-Encoding: chunked'
]);
$curl->setopt(CURLOPT_READFUNCTION, sub {
    return POST(undef, Content_Type => 'multipart/form-data', Content => [
        name    => 'upload',
        action  => 'keep',
        backup1 => [ '/tmp/backup1.zip' ],   # 1st file for upload
    ])->content;
});
my $r = $curl->perform;
3 голосов
/ 04 июля 2012

Обратный вызов CURLOPT_READFUNCTION используется только для кодирования по частям передачи.Это может работать, но я не смог получить его и обнаружил, что это все равно не нужно.

Мой вариант использования был для загрузки данных в AWS, гдене нормально загружать данные как данные из нескольких частей.Вместо этого это прямое ПОСТ данных.Это требует, чтобы вы знали, сколько данных вы отправляете на сервер.Кажется, это работает для меня:

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.После этого базовая библиотека curl обрабатывает чтение из нее без каких-либо обратных вызовов.

...