File Coverage

blib/lib/Fetch/Image.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Fetch::Image;
2 1     1   47961 use strict;
  1         3  
  1         36  
3 1     1   6 use warnings;
  1         2  
  1         28  
4              
5 1     1   2419 use LWP::UserAgent::Paranoid;
  0            
  0            
6             use Data::Validate::Image;
7             use Data::Validate::URI qw/is_web_uri/;
8             use File::Temp;
9             use Exception::Simple;
10             use URI;
11              
12             our $VERSION = '1.000000';
13             $VERSION = eval $VERSION;
14              
15             sub new{
16             my ( $invocant, $config ) = @_;
17              
18             my $class = ref( $invocant ) || $invocant;
19             my $self = {};
20             bless( $self, $class );
21              
22             $self->{'image_validator'} = Data::Validate::Image->new;
23              
24             $self->{'config'} = $config;
25              
26             # setup some defaults
27             if ( !defined($self->{'config'}->{'max_filesize'}) ){
28             $self->{'config'}->{'max_filesize'} = 524_288;
29             }
30              
31             # default allowed image types if none defined
32             if ( !defined($self->{'config'}->{'allowed_types'}) ){
33             $self->{'config'}->{'allowed_types'} = {
34             'image/png' => 1,
35             'image/jpg' => 1,
36             'image/jpeg' => 1,
37             'image/pjpeg' => 1,
38             'image/bmp' => 1,
39             'image/gif' => 1,
40             };
41             }
42              
43             return $self;
44             }
45              
46             sub fetch{
47             my ( $self, $url ) = @_;
48              
49             my $uri = URI->new( $url );
50             $url = $uri->as_string;
51              
52             if ( !defined( $url ) ){
53             Exception::Simple->throw("no url");
54             } elsif ( !defined( is_web_uri( $url ) ) ){
55             Exception::Simple->throw("invalid url");
56             }
57              
58             my $ua = $self->_setup_ua( $url );
59              
60             my $head = $self->_head( $ua, $url );
61             return $self->_save( $ua, $url )
62             || Exception::Simple->throw("generic error");
63             }
64              
65             sub _setup_ua{
66             my ( $self, $url ) = @_;
67              
68             my $ua = LWP::UserAgent::Paranoid->new;
69              
70             if ( defined( $self->{'config'}->{'user_agent'} ) ){
71             $ua->agent( $self->{'config'}->{'user_agent'} );
72             }
73              
74             if ( defined( $self->{'config'}->{'timeout'} ) ){
75             $ua->timeout( $self->{'config'}->{'timeout'} );
76             }
77             $ua->cookie_jar( {} ); #don't care for cookies
78              
79             $ua->default_header( 'Referer' => $url ); #naughty, maybe, but will get around 99% of anti-leach protection :D
80              
81             return $ua;
82             }
83              
84             # returns a HTTP::Response for a HTTP HEAD request
85             sub _head{
86             my ( $self, $ua, $url ) = @_;
87              
88             my $head = $ua->head( $url );
89              
90             $head->is_error && Exception::Simple->throw("transfer error");
91              
92             exists( $self->{'config'}->{'allowed_types'}->{ $head->header('content-type') } )
93             || Exception::Simple->throw("invalid content-type");
94              
95             if (
96             $head->header('content-length')
97             && ( $head->header('content-length') > $self->{'config'}->{'max_filesize'} )
98             ){
99             #file too big
100             Exception::Simple->throw("filesize exceeded");
101             }
102              
103             return $head;
104             }
105              
106             # returns a File::Temp copy of the requested url
107             sub _save{
108             my ( $self, $ua, $url ) = @_;
109              
110             my $response = $ua->get( $url )
111             || Exception::Simple->throw("download Failed");
112              
113             my $temp_file = File::Temp->new
114             || Exception::Simple->throw("temp file save failed");
115             $temp_file->print( $response->content );
116             $temp_file->close;
117              
118             my $image_info = $self->{'image_validator'}->validate($temp_file->filename);
119              
120             if ( !$image_info ){
121             $temp_file->DESTROY;
122             Exception::Simple->throw("not an image");
123             };
124              
125             $image_info->{'temp_file'} = $temp_file;
126             return $image_info;
127             }
128              
129             1;
130              
131             =head1 NAME
132              
133             Fetch::Image - fetch a remote image into a L
134              
135             =head1 SYNOPSIS
136              
137             use Fetch::Image;
138             use Try::Tiny; #or just use eval {}, it's all good
139              
140             my $fetcher = Fetch::Image->new( {
141             'max_filesize' => 524_288,
142             'user_agent' => 'mozilla firefox or something...',
143             'allowed_types' => {
144             'image/png' => 1,
145             'image/jpg' => 1,
146             'image/jpeg' => 1,
147             'image/pjpeg' => 1,
148             'image/bmp' => 1,
149             'image/gif' => 1,
150             },
151             } );
152              
153             my $image_info = try{
154             $fetcher->fetch('http://www.google.com/logos/2011/trevithick11-hp.jpg');
155             } catch {
156             #error gets caught here...
157             warn $_; #this
158             warn $_->error; #or this
159             };
160              
161             use Data::Dumper;
162             warn Dumper( $image_info );
163              
164             #the image is now a Temp::File in $image_info->{'temp_file'};
165              
166             =head1 DESCRIPTION
167              
168             Class that will fetch a remote image and return a hash of the image_info and the L
169              
170             =head1 METHODS
171              
172             =head2 new
173              
174             takes 3 options
175              
176             my $fetcher = Fetch::Image->new( {
177             'max_filesize' => 524_288, #default value (bytes)
178             'user_agent' => 'mozilla firefox or something...',
179             'allowed_types' => { #allowed content types (default all of these)
180             'image/png' => 1,
181             'image/jpg' => 1,
182             'image/jpeg' => 1,
183             'image/pjpeg' => 1,
184             'image/bmp' => 1,
185             'image/gif' => 1,
186             },
187             } );
188              
189             =head2 fetch
190              
191             takes 1 argument, the url of the image to fetch
192              
193             returns a hash of the image info, from L, with an extra property, 'temp_file' which is the L
194              
195             =head1 AUTHORS
196              
197             Mark Ellis Emarkellis@cpan.orgE
198              
199             =head1 SEE ALSO
200              
201             L, L
202              
203             =head1 LICENSE
204              
205             Copyright 2014 by Mark Ellis Emarkellis@cpan.orgE
206              
207             This library is free software, you can redistribute it and/or modify
208             it under the same terms as Perl itself.
209              
210             =cut