sub
new {
my
(
$class
,
%args
) =
@_
;
my
$s
=
bless
{
data
=> [ ],
page_number
=> 0,
idx
=> 0,
%args
,
},
$class
;
$s
->_init;
return
$s
;
}
sub
_init {
my
(
$s
) =
@_
;
foreach
(
qw( bucket page_size page_number )
) {
confess
"Required argument '$_' was not provided"
unless
$s
->{
$_
};
}
$s
->{page_number}--;
$s
->{marker} =
''
unless
defined
(
$s
->{marker} );
$s
->{__fetched_first_page} = 0;
$s
->{data} = [];
$s
->{pattern} ||=
qr(.*)
;
}
sub
marker {
shift
->{marker} }
sub
pattern {
shift
->{pattern} }
sub
bucket {
shift
->{bucket} }
sub
page_size {
shift
->{page_size} }
sub
prefix {
shift
->{prefix} }
sub
has_prev {
my
$s
=
shift
;
return
$s
->page_number > 1;
}
sub
has_next {
shift
->{has_next} }
sub
next
{
my
$s
=
shift
;
if
(
exists
(
$s
->{data}->[
$s
->{idx} ] ) ) {
return
$s
->{data}->[
$s
->{idx}++ ];
}
else
{
if
(
my
$page
=
$s
->next_page ) {
$s
->{data} =
$page
;
$s
->{idx} = 0;
return
$s
->{data}->[
$s
->{idx}++ ];
}
else
{
return
;
}
}
}
sub
reset
{
my
$s
=
shift
;
$s
->{idx} = 0;
}
sub
page_number {
my
$s
=
shift
;
@_
?
$s
->{page_number} =
$_
[0] - 1 :
$s
->{page_number};
}
sub
next_page {
my
$s
=
shift
;
if
( ( !
$s
->{__fetched_first_page}++ ) &&
$s
->page_number ) {
my
$start_page
=
$s
->page_number;
my
$to_discard
=
$start_page
*
$s
->page_size;
my
$discarded
= 0;
while
( 1 ) {
my
$item
=
$s
->_next
or
last
;
$discarded
++
if
$item
->{key} =~
$s
->pattern;
last
if
$discarded
>
$to_discard
;
}
}
my
@chunk
= ();
while
(
my
$item
=
$s
->_next() ) {
next
unless
$item
->{key} =~
$s
->pattern;
push
@chunk
,
$item
;
last
if
@chunk
==
$s
->page_size;
}
my
@out
=
map
{
my
$owner
= AWS::S3::Owner->new( %{
$_
->{owner} } );
delete
$_
->{owner};
AWS::S3::File->new(
%$_
,
owner
=>
$owner
);
}
@chunk
;
$s
->{page_number}++;
return
unless
@out
;
wantarray
?
@out
: \
@out
;
}
sub
_next {
my
$s
=
shift
;
if
(
my
$item
=
shift
( @{
$s
->{data} } ) ) {
return
$item
;
}
else
{
if
(
my
@chunk
=
$s
->_fetch() ) {
push
@{
$s
->{data} },
@chunk
;
return
shift
( @{
$s
->{data} } );
}
else
{
return
;
}
}
}
sub
_fetch {
my
(
$s
) =
@_
;
my
$path
=
$s
->{bucket}->name .
'/'
;
my
%params
= ();
$params
{marker} =
$s
->{marker}
if
$s
->{marker};
$params
{prefix} =
$s
->{prefix}
if
$s
->{prefix};
$params
{max_keys} = 1000;
$params
{delimiter} =
$s
->{delimiter}
if
$s
->{delimiter};
my
$type
=
'ListBucket'
;
my
$request
=
$s
->{bucket}->s3->request(
$type
,
%params
,
bucket
=>
$s
->{bucket}->name );
my
$response
=
$request
->request();
$s
->{has_next} = (
$response
->xpc->findvalue(
'//s3:IsTruncated'
) ||
''
) eq
'true'
? 1 : 0;
my
@files
= ();
foreach
my
$node
(
$response
->xpc->findnodes(
'//s3:Contents'
) ) {
my
(
$owner_node
) =
$response
->xpc->findnodes(
'.//s3:Owner'
,
$node
);
my
$owner
= {
id
=>
$response
->xpc->findvalue(
'.//s3:ID'
,
$owner_node
),
display_name
=>
$response
->xpc->findvalue(
'.//s3:DisplayName'
,
$owner_node
)
};
my
$etag
=
$response
->xpc->findvalue(
'.//s3:ETag'
,
$node
);
push
@files
,
{
bucket
=>
$s
->{bucket},
key
=>
$response
->xpc->findvalue(
'.//s3:Key'
,
$node
),
lastmodified
=>
$response
->xpc->findvalue(
'.//s3:LastModified'
,
$node
),
etag
=>
$response
->xpc->findvalue(
'.//s3:ETag'
,
$node
),
size
=>
$response
->xpc->findvalue(
'.//s3:Size'
,
$node
),
owner
=>
$owner
,
};
}
if
(
@files
) {
$s
->{marker} =
$files
[-1]->{key};
}
return
unless
defined
wantarray
;
@files
?
return
@files
:
return
;
}
1;