2012-02-28 34 views
5

tôi sử dụng WWW :: Curl để tải lên tập tin:www :: curl - làm thế nào để tải lên (bài) các tập tin lớn

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); 

có gì đặc biệt ở đây và mã này hoạt động tốt.

Tôi muốn tải lên các tệp lớn và tôi không muốn tải trước mọi thứ trong bộ nhớ. Ít nhất đó là những gì tôi nghe nói rằng libcurl đang làm.

CURLOPT_READFUNCTION chấp nhận cuộc gọi lại trả về các phần của nội dung. Điều đó có nghĩa rằng tôi không thể sử dụng WWW :: Curl :: Form để thiết lập các thông số POST nhưng tôi phải trả lại toàn bộ nội dung thông qua cuộc gọi lại này. Có đúng không?

Tôi nghĩ rằng mã có thể trông như thế này:

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} ? '@'[email protected]{$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; 
}); 

Những dữ liệu không CURLOPT_READFUNCTION gọi lại phải quay trở lại? $ params + File (s) nội dung? Ở định dạng nào?

Tôi có thực sự phải tự tạo dữ liệu (được trả về bởi CURLOPT_READFUNCTION) hoặc có cách đơn giản để tạo nó theo đúng định dạng không?

Cảm ơn

+0

Bạn có cam kết sử dụng WWW :: Curl không? Tôi nghĩ rằng điều này sẽ dễ dàng hơn với LWP, nếu bạn có thể chuyển đổi. – wes

+0

LWP hoặc sử dụng tốt hơn WWW :: Cơ chế –

+0

Tôi biết câu trả lời này không liên quan trực tiếp đến mã của bạn, nhưng tôi đã dành một lượng đáng kể thời gian khắc phục sự cố tương tự bằng cách sử dụng WWW :: Cơ chế chỉ để phát hiện ra MaxPostSize trên máy chủ web được quản trị viên của chúng tôi đặt cho một số giới hạn tùy ý. – AWT

Trả lời

4

Kiểm tra 16formpost.t là có liên quan. Như bạn thấy, nó hoàn toàn bị vô hiệu hóa. Thực tế này và các thí nghiệm không kết quả của tôi với các giá trị trả về khác nhau cho hàm gọi lại cho phép tôi tin rằng tính năng CURLOPT_READFUNCTION được biết là bị hỏng trong ràng buộc Perl.

Tôi phải trả lại toàn bộ nội dung thông qua cuộc gọi lại này. Có đúng không?

Không, bạn có thể cấp dữ liệu cho phần thân yêu cầu, phù hợp với mã hóa chunked. Cuộc gọi lại sẽ nhất thiết phải được gọi nhiều lần, theo giới hạn được đặt trong CURLOPT_INFILESIZE.

Dữ liệu nào CURLOPT_READFUNCTION gọi lại phải trả lại?

Cơ quan yêu cầu HTTP. Vì bạn tải tệp lên, điều này có nghĩa là Content-Type multipart/form-data. Sau đây là một ví dụ sử dụng HTTP :: Message. CURLOPT_HTTPPOST là một cách khác để xây dựng định dạng này.

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; 
+0

cảm ơn. thông tin rất hữu ích. Vấn đề với HTTP :: Thông điệp là nó tải toàn bộ nội dung trong bộ nhớ. Tôi có bộ nhớ giới hạn (64MB) và điều đó có nghĩa là yêu cầu phải nhỏ hơn nhiều. Tôi sẽ cố gắng ngày hôm nay để tạo ra một hàm gọi lại trả về nội dung theo khối mà không cần đệm toàn bộ nội dung. – toktok

+0

vừa nhận được từ WWW :: Người bảo trì Curl về tính năng CURLOPT_READFUNCTION: "vâng, trông khá bị hỏng. Tôi đang lên kế hoạch thực hiện WWW :: Đại tu Curl trong những tuần tới, cũng có thể sửa lỗi này." – daxim

+0

Tại sao bạn nghĩ rằng nó bị hỏng? Tôi không có vấn đề với READFUNCTION, nó hoạt động chính xác như được giải thích trên trang chủ curl. "nó hoạt động cho tôi" ;-) – toktok

3

Gọi lại CURLOPT_READFUNCTION chỉ được sử dụng cho mã hóa chuyển mã chunked. Nó có thể hoạt động, nhưng tôi chưa thể thực hiện được và thấy rằng làm như vậy là không cần thiết.

Trường hợp sử dụng của tôi là để tải dữ liệu lên AWS, nơi không tải lên dữ liệu dưới dạng dữ liệu biểu mẫu nhiều phần. Thay vào đó, đó là POST trực tiếp của dữ liệu. Nó đòi hỏi rằng bạn biết bao nhiêu dữ liệu bạn đang gửi máy chủ, mặc dù. Điều này có vẻ phù hợp với tôi:

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"); 
} 

Điều quan trọng là cung cấp tham chiếu tập tin mở tới CURLOPT_READDATA. Sau đó, thư viện curl lõi xử lý các lần đọc từ nó mà không cần bất kỳ callback nào.

Các vấn đề liên quan