Skip to content

Instantly share code, notes, and snippets.

@dolmen
Last active December 21, 2015 12:29
Show Gist options
  • Save dolmen/6306377 to your computer and use it in GitHub Desktop.
Save dolmen/6306377 to your computer and use it in GitHub Desktop.
Hi-jacking File::Fetch API for AnyEvent-backed parallel file download
#!/usr/bin/perl
use 5.014;
use strict;
use warnings;
{
package File::Fetch::AnyEvent::HTTP;
use parent 'File::Fetch';
use AnyEvent::HTTP;
#
# Returns an AnyEvent condvar that will be triggered when the download is done.
#
# Get data in $content:
# $ff->fetch(to => \my $content)->recv;
# Get data save as a file in $dir:
# $file = $ff->fetch(to => $dir)->recv;
#
sub fetch
{
my ($self, $TO, $to) = @_;
die if @_ > 2 && ($TO ne 'to' || !defined $to);
my $cv = AE::cv;
# Called as $ff->fetch(to => \my $content)->recv;
if (ref $to) {
http_get $self->uri,
on_body => sub {
$$to .= $_[0];
return 1
},
sub {
$cv->send
};
return $cv
}
$to = Cwd::cwd() unless defined $to;
unless (-d $to) {
mkpath($to) or die;
}
my $file = File::Spec->catfile($to, $self->output_file);
open my $f, '>:raw', $file or die;
# TODO The returned cv should also be used to track cancellation
# (if the caller drops the $cv, the request should be properly
# cancelled)
# TODO Handle redirects transparently
http_get $self->uri,
on_body => sub {
print $f $_[0];
return 1
},
sub {
close $f;
$cv->send($file);
};
return $cv
}
}
use URI;
# Get the CSS file
my $css_uri = 'http://glyphicons.com/wp-content/themes/glyphicons/sk/public/css/style-new.css?ver=1.0';
my $ff = File::Fetch::AnyEvent::HTTP->new(uri => $css_uri);
my $cv = $ff->fetch();
my $css_file = $cv->recv;
my $css = do { local $/; open my $f, '<', $css_file or die; <$f> };
$cv->recv;
die unless $css;
# Extract the fonts URLs from the CSS content
my @urls = $css =~ m<
url\(
'?
(
.+?
/fonts/
[^')]+
# \. (?: eot | woff | ttf )
)
(?: \# .*? )?
'?
\)
>gx;
# cleanup
undef $css;
$cv = AE::cv;
# Fetch the font files
my %fetching;
foreach my $rel_url (@urls) {
my $url = URI->new_abs($rel_url, $css_uri);
$url->fragment(undef);
$ff = File::Fetch::AnyEvent::HTTP->new(uri => $url);
my $output_file = $ff->output_file;
say "$url => ", $ff->output_file;
if (exists $fetching{$output_file}) {
warn "$output_file duplicate!\n";
next
}
$cv->begin;
my $file_cv = $ff->fetch();
$fetching{$output_file} = $file_cv;
$file_cv->cb(sub {
say "$output_file: done.";
delete $fetching{$output_file};
$cv->end;
});
}
# Start the event loop, and the downloads
$cv->recv;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment