diff options
Diffstat (limited to 'perllib/FixMyStreet/App/Model/PhotoSet.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Model/PhotoSet.pm | 140 |
1 files changed, 43 insertions, 97 deletions
diff --git a/perllib/FixMyStreet/App/Model/PhotoSet.pm b/perllib/FixMyStreet/App/Model/PhotoSet.pm index 8fcc1700e..58b352c73 100644 --- a/perllib/FixMyStreet/App/Model/PhotoSet.pm +++ b/perllib/FixMyStreet/App/Model/PhotoSet.pm @@ -3,21 +3,18 @@ package FixMyStreet::App::Model::PhotoSet; # TODO this isn't a Cat model, rename to something else use Moose; -use Path::Tiny 'path'; - -my $IM = eval { - require Image::Magick; - Image::Magick->import; - 1; -}; use Scalar::Util 'openhandle', 'blessed'; -use Digest::SHA qw(sha1_hex); use Image::Size; use IPC::Cmd qw(can_run); use IPC::Open3; use MIME::Base64; +use FixMyStreet; +use FixMyStreet::ImageMagick; +use FixMyStreet::PhotoStorage; + +# Attached Catalyst app, if present, for feeding back errors during photo upload has c => ( is => 'ro', ); @@ -57,27 +54,28 @@ has data_items => ( # either a) split from db_data or b) provided by photo uploa my $self = shift; my $data = $self->db_data or return []; - return [$data] if (detect_type($data)); + return [$data] if ($self->storage->detect_type($data)); return [ split ',' => $data ]; }, ); -has upload_dir => ( +has storage => ( is => 'ro', lazy => 1, default => sub { - path(FixMyStreet->config('UPLOAD_DIR'))->absolute(FixMyStreet->path_to()); - }, + return FixMyStreet::PhotoStorage::backend; + } ); -sub detect_type { - return 'jpeg' if $_[0] =~ /^\x{ff}\x{d8}/; - return 'png' if $_[0] =~ /^\x{89}\x{50}/; - return 'tiff' if $_[0] =~ /^II/; - return 'gif' if $_[0] =~ /^GIF/; - return ''; -} +has symlinkable => ( + is => 'ro', + lazy => 1, + default => sub { + my $cfg = FixMyStreet->config('PHOTO_STORAGE_OPTIONS'); + return $cfg ? $cfg->{SYMLINK_FULL_SIZE} : 0; + } +); =head2 C<ids>, C<num_images>, C<get_id>, C<all_ids> @@ -166,25 +164,21 @@ has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data proc return (); } - # we have an image we can use - save it to the upload dir for storage - my $fileid = $self->get_fileid($photo_blob); - my $file = $self->get_file($fileid, $type); - $upload->copy_to( $file ); - return $file->basename; - + # we have an image we can use - save it to storage + $photo_blob = FixMyStreet::ImageMagick->new(blob => $photo_blob)->shrink('2048x2048')->as_blob; + return $self->storage->store_photo($photo_blob); } - if (my $type = detect_type($part)) { + + # It might be a raw file stored in the DB column... + if (my $type = $self->storage->detect_type($part)) { my $photo_blob = $part; - my $fileid = $self->get_fileid($photo_blob); - my $file = $self->get_file($fileid, $type); - $file->spew_raw($photo_blob); - return $file->basename; + return $self->storage->store_photo($photo_blob); + # TODO: Should this update the DB record with a pointer to the + # newly-stored file, instead of leaving it in the DB? } - my ($fileid, $type) = split /\./, $part; - $type ||= 'jpeg'; - if ($fileid && length($fileid) == 40) { - my $file = $self->get_file($fileid, $type); - $file->basename; + + if (my $key = $self->storage->validate_key($part)) { + $key; } else { # A bad hash, probably a bot spamming with bad data. (); @@ -194,25 +188,13 @@ has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data proc }, ); -sub get_fileid { - my ($self, $photo_blob) = @_; - return sha1_hex($photo_blob); -} - -sub get_file { - my ($self, $fileid, $type) = @_; - my $cache_dir = $self->upload_dir; - return path( $cache_dir, "$fileid.$type" ); -} - sub get_raw_image { my ($self, $index) = @_; my $filename = $self->get_id($index); - my ($fileid, $type) = split /\./, $filename; - my $file = $self->get_file($fileid, $type); - if ($file->exists) { - my $photo = $file->slurp_raw; + my ($photo, $type, $object) = $self->storage->retrieve_photo($filename); + if ($photo) { return { + $object ? (object => $object) : (), data => $photo, content_type => "image/$type", extension => $type, @@ -229,14 +211,21 @@ sub get_image_data { my $photo = $image->{data}; my $size = $args{size}; + + if ($self->symlinkable && $image->{object} && $size eq 'full') { + $image->{symlink} = delete $image->{object}; + return $image; + } + + my $im = FixMyStreet::ImageMagick->new(blob => $photo); if ( $size eq 'tn' ) { - $photo = _shrink( $photo, 'x100' ); + $photo = $im->shrink('x100')->as_blob; } elsif ( $size eq 'fp' ) { - $photo = _crop( $photo ); + $photo = $im->crop->as_blob; } elsif ( $size eq 'full' ) { # do nothing } else { - $photo = _shrink( $photo, $args{default} || '250x250' ); + $photo = $im->shrink($args{default} || '250x250')->as_blob; } return { @@ -298,7 +287,7 @@ sub rotate_image { return if $index > $#images; my $image = $self->get_raw_image($index); - $images[$index] = _rotate_image( $image->{data}, $direction ); + $images[$index] = FixMyStreet::ImageMagick->new(blob => $image->{data})->rotate($direction)->as_blob; my $new_set = (ref $self)->new({ data_items => \@images, @@ -310,47 +299,4 @@ sub rotate_image { return $new_set->data; # e.g. new comma-separated fileid } -sub _rotate_image { - my ($photo, $direction) = @_; - return $photo unless $IM; - my $image = Image::Magick->new; - $image->BlobToImage($photo); - my $err = $image->Rotate($direction); - return 0 if $err; - my @blobs = $image->ImageToBlob(); - undef $image; - return $blobs[0]; -} - - -# Shrinks a picture to the specified size, but keeping in proportion. -sub _shrink { - my ($photo, $size) = @_; - return $photo unless $IM; - my $image = Image::Magick->new; - $image->BlobToImage($photo); - my $err = $image->Scale(geometry => "$size>"); - throw Error::Simple("resize failed: $err") if "$err"; - $image->Strip(); - my @blobs = $image->ImageToBlob(); - undef $image; - return $blobs[0]; -} - -# Shrinks a picture to 90x60, cropping so that it is exactly that. -sub _crop { - my ($photo) = @_; - return $photo unless $IM; - my $image = Image::Magick->new; - $image->BlobToImage($photo); - my $err = $image->Resize( geometry => "90x60^" ); - throw Error::Simple("resize failed: $err") if "$err"; - $err = $image->Extent( geometry => '90x60', gravity => 'Center' ); - throw Error::Simple("resize failed: $err") if "$err"; - $image->Strip(); - my @blobs = $image->ImageToBlob(); - undef $image; - return $blobs[0]; -} - 1; |