Laravel Image thumb - php

I making a web app where uploaded photos are stored in /app/storage.
To show the file I am using a route ex: showphoto/{id} (Paths are stored in DB)
public function showphoto($id){
$photo = Photo::findOrFail($id);
return $this->getFile($photo);
}
private function getFile($f){
if($f->path){
$file = storage_path($f->path.'/'.$f->origin_name);
if (File::exists($file)) {
$contents = File::get($file);
switch($f->ext) {
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "jpeg":
case "jpg": $ctype="image/jpeg"; break;
case "pdf": $ctype="application/pdf"; break;
default:
}
$response = Response::make($contents, 200);
$response->header('Content-Type', $ctype);
return $response;
}
}
}
To show image I am using
{{ HTML::image(route('showphoto', $photo->id), $photo->getName(), array('class'=>'img-thumbnail', 'width' => '100', 'height'=>'100')) }}
Question: Some file are more then 2 MB and when I have a list of them they are loading very slowly, So on the list I want to show just a Thumb of the photo.
It is possible to create a temporary thumb that will not be stored anywhere?
Or it is not a good idea to create a temporary thumb each time I load a page.
Thank you in advance.

If you have a bunch of large images that you are going to be processing in some way when a page is loaded you're probably going to have a bad time. All that extra processing is going to really slow everything down.
What you could do is create a thumbnail for an image when it is uploaded and store that somewhere. That way, you need only load your thumbnails instead of the larger images.
Alternatively, if you prefer to be able to specify the size of the thumbnail in your page another solution could be to generate your thumbnails at your specified size when the page loads. You'll need to make sure that you cache these thumbnails so you can use just load them in going forward though. If you opt for this approach the first time you load your page will probably take a while, but after that, subsequent page loads will be much quicker, since it will be using pre-created, cached images.
A great package to use with Laravel when manipulating images is Intervention Image:
http://image.intervention.io/

Related

What should I do to make specific images show for certain file extension types?

I'm trying to implement a php, mySQL Admin panel/dashboard which shows a small preview image of the file that has been uploaded, if the file is not a jpg, jpeg, png or gif then I would like to display a placeholder image.
So for example if the uploaded file is actually a jpg, the preview column in the table would show the image associated to the file name stored in the db (this I have working) but if it's a pdf then it should show a stock/filler/predefined image of my choosing.
I'm hoping to do this in php but have very limited knowledge in this area with this project being my first large php project.
I have tried to implement this functionality using a js/jquery switch but have had no such luck, Code for the last attempt included below:
$(document).ready(function() {
var fileName, fileExtension;
fileName = event.target.innerHTML;
fileExtension = fileName.replace(/^.*\./, '');
switch (fileExtension) {
case 'png': case 'jpeg': case 'jpg':
$('#tableImg').attr("src","<?php echo $row["fileName"]; ?>");
break;
case 'zip':
$('#tableImg').attr("src","images/pdf.png");
break;
case 'pdf':
$('#tableImg').attr("src","images/pdf.png");
break;
}
});
I am no longer using this method, it was just my last attempt to get it working.
The preview column is currently called in the php file like so;
<img id="tableImg" class="admin-thumb" src="<?php echo $row["fileName"]; ?>" alt="">
I know that my current methodology will only allow the files with jpg, png and other image related extensions to show but I can't figure out how to swap out the echo $row["filename"]
What I think I'm after, and please correct me if I'm wrong is either an if, else if, else or a php switch. I just have no idea how to make it work the way mentioned earlier.
To recap:
I need an assist making a preview column of a table show either the image associated to the file name/URL or if the file type is a zip, pdf or non image file it shows a placeholder image selected by me. I did have a screenshot to share but I don't have 10 rep to do so, yet.
Any help is appreciated and my thanks in advance.
Limur
SplFileInfo should provide the extention and then just use a switch statement to set the image you want to show.
$myfile=$row["fileName"];
$info = new SplFileInfo($myfile);
$ext = $info->getExtension();
switch ($ext) {
case 'zip':
$image='zip.png';
break;
case 'pdf':
$image='pdf.png';
break;
default:
$image=$myFile;
}
<img id="tableImg" class="admin-thumb" src="<?= $image ?>" alt="">

ImageMagick and PHP to properly rotate an image

I am trying to use Imagick in my php code to properly orient images, so that I can strip the metadata from them and have them properly show up. I tried the methodology in this post:
Detect EXIF Orientation and Rotate Image using ImageMagick
But it is not working. Using the autorotate function in that post, this is what I am doing:
$working_image = new \Imagick();
$working_image->readImageBlob( $source_data);
$working_image->setImageFormat('jpeg');
autorotate($working_image);
... some resizing code:
$working_image->resizeImage( $width, $height, \Imagick::FILTER_CATROM, .7);
// get rid of metadata
$working_image->stripImage();
$working_image->writeImage( <unique filename> );
$working_image->getImageBlob();
... write out to data file to google
We're using Google's cloud stuff to store our files, hence the "getImageBlob" call.
Problem is that this doesn't seem to work. The resultant images are still oriented the same way they were, but now have no metadata, so the tag won't "fix" them.
What am I doing wrong here? I am using this version of the Imagick PHP object:
[versionNumber] => 1673
[versionString] => ImageMagick 6.8.9-9 Q16 x86_64 2015-01-05 http://www.imagemagick.org
In response to your answer:
Thanks for the clarifications. Looking at one of the specific images that I am having problems with, identify -verbose shows multiple places defining orientation:
Orientation: RightTop
exif:Orientation: 6
exif:thumbnail:Orientation: 6
And this is reflected in my call to Imagick::getImageOrientation in PHP, which returns the "6" value, which then causes my code to call functions like "Imagick::rotateImage("#000", 180);". The problem is that the image is unchanged by these calls. 8-(
The core problem is this: We get these images by the thousands every day from MLS's all over the country. We currently process them (resizing currently) via an automatic process. When they are displayed on our site, everything is currently fine, because the <img> tag seems to be happy to interpret the EXIF data and show the image in proper orientation. BUT... we want to start optimizing images, which means stripping the EXIF info out. So I need to figure out why Imagick->rotateImage() isn't working.
Ok, let's write a whole answer:)
What I meant with the link to ImageMagick's -auto-orient, was to point out, that this kind of auto-orientation depends on EXIF profile and "Orientation" setting. If it's missing or is wrong the auto-orientation will not work as expected. To check whether your images has the EXIF profile with Orientation in tact, you can use several ways:
PHP Imagick (check it with image before you call ->stripImage())
echo 'Orientation is: ', $working_image->getImageOrientation();
PHP (if you have local file)
$exif = exif_read_data('input.jpg');
echo 'Orientation is: ', isset($exif['Orientation']) ? $exif['Orientation'] : 'missing';
Online EXIF reader.
The values you get are described for example here or more about image auto-rotation topic and sample images are here
Or you can use programs like Gimp or Photoshop.
So you're doing nothing wrong, without EXIF this won't work. It's camera that is writing the tags into images, so there is no guarantee all your photos have it.
Perhaps offer image rotation for visitors in your website instead? There is now CSS3 transform: rotate(deg) property that makes it really easy, see....
We can get the current image orientation and can easily update to the original one with Imagick
public function fix_image_orientation()
{
$working_image = new \Imagick();
$working_image->readImageBlob($source_data);
$working_image->setImageFormat('jpeg');
$orientation = $working_image->getImageOrientation();
if (!empty($orientation)) {
switch ($orientation) {
case imagick::ORIENTATION_BOTTOMRIGHT:
$working_image->rotateimage("#000", 180);
break;
case imagick::ORIENTATION_RIGHTTOP:
$working_image->rotateimage("#000", 90);
break;
case imagick::ORIENTATION_LEFTBOTTOM:
$working_image->rotateimage("#000", -90);
break;
}
}
$working_image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
$working_image->writeImage( <unique filename> );
}
#Satendra Rawat posted a good, but incomplete answer. There is the GitHub repository https://github.com/ianare/exif-samples, where you can see 8 orientation examples. Only 3, 5 and 8 worked correctly.
So here is a update for all the orientations:
public function fix_image_orientation()
{
$working_image = new \Imagick();
$working_image->readImageBlob($source_data);
$working_image->setImageFormat('jpeg');
$orientation = $working_image->getImageOrientation();
if (!empty($orientation)) {
switch ($orientation) {
default:
case imagick::ORIENTATION_TOPLEFT:
break;
case imagick::ORIENTATION_TOPRIGHT:
$working_image->flipImage();
$working_image->rotateImage("#000", 180);
break;
case imagick::ORIENTATION_BOTTOMRIGHT:
$working_image->rotateImage("#000", 180);
break;
case imagick::ORIENTATION_BOTTOMLEFT:
$working_image->flipImage();
break;
case imagick::ORIENTATION_LEFTTOP:
$working_image->rotateImage("#000", -90);
$working_image->flipImage();
break;
case imagick::ORIENTATION_RIGHTTOP:
$working_image->rotateImage("#000", 90);
break;
case imagick::ORIENTATION_RIGHTBOTTOM:
$working_image->rotateImage("#000", 90);
$working_image->flipImage();
break;
case imagick::ORIENTATION_LEFTBOTTOM:
$working_image->rotateImage("#000", -90);
break;
}
}
$working_image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
$working_image->writeImage( <unique filename> );
}

Right way of watermarking & storing & displaying images in PHP

I'm building a web based system, which will host loads and loads of highres images, and they will be available for sale. Of course I will never display the highres image, instead when browsing people will only see a low resolution, watermarked image. Currently the workflow is as follows:
PHP script handles the highres image upload, when image is uploaded, it's automatically re-sized to a low res image and to a thumbnail image as well and both of the files are saved on the server, (no watermark is added).
When people are browsing, the page displays the thumbnail of the image, on click, it enlarges and displays the lowres image with watermark as well. At the time being I apply the watermark on the fly whenever the lowres image is opened.
My question is, what is the correct way:
1) Should I save a 2nd copy of the lowres image with thumbnail, only when it's access for the first time? I mean if somebody access the image, I add the watermark on the fly, then display the image & store it on the server. Next time the same image is accessed if a watermarked copy exist just display the wm copy, otherwise apply watermark on the fly. (in case watermark.png is changed, just delete the watermarked images and they will be recreated as accessed).
2) Should I keep applying watermarks on the fly like I'm doing now.
My biggest question is how big is the difference between a PHP file_exists(), and adding a watermark to an image, something like:
$image = new Imagick();
$image->readImage($workfolder.$event . DIRECTORY_SEPARATOR . $cat . DIRECTORY_SEPARATOR .$mit);
$watermark = new Imagick();
$watermark->readImage($workfolder.$event . DIRECTORY_SEPARATOR . "hires" . DIRECTORY_SEPARATOR ."WATERMARK.PNG");
$image->compositeImage($watermark, imagick::COMPOSITE_OVER, 0, 0);
All lowres images are 1024x1024, JPG with a quality setting of 45%, and all unnecessary filters removed, so the file size of a lowres image is about 40Kb-80Kb.
It is somehow related to this question, just the scale and the scenarios is a bit different.
I'm on a dedicated server (Xeon E3-1245v2) cpu, 32 GB ram, 2 TB storage), the site does not have a big traffic overall, but it has HUGE spikes from time to time. When images are released we get a few thousand hits per hours with people browsing trough the images, downloading, purchasing, etc. So while on normal usage I'm sure that generating on the fly is the right approach, I'm a bit worried about the spike period.
Need to mention that I'm using ImageMagick library for image processing, not GD.
Thanks for your input.
UPDATE
None of the answers where a full complete solution, but that is good since I never looked for that. It was a hard decision which one to accept and whom to accord the bounty.
#Ambroise-Maupate solution is good, but yet it's relay on the PHP to do the job.
#Hugo Delsing propose to use the web server for serving cached files, lowering the calls to PHP script, which will mean less resources used, on the other hand it's not really storage friendly.
I will use a mixed-merge solution of the 2 answers, relaying on a CRON job to remove the garbage.
Thanks for the directions.
Personally I would create a static/cookieless subdomain in a CDN kinda way to handle these kind of images. The main reasons are:
Images are only created once
Only accessed images are created
Once created, an image is served from cache and is a lot faster.
The first step would be to create a website on a subdomain that points to an empty folder. Use the settings for IIS/Apache or whatever to disable sessions for this new website. Also set some long caching headers on the site, because the content shouldn't change
The second step would be to create an .htaccess file containing the following.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) /create.php?path=$1 [L]
This will make sure that if somebody would access an existing image, it will show the image directly without PHP interfering. Every non-existing request will be handled by the create.php script, which is the next thing you should add.
<?php
function NotFound()
{
if (!headers_sent()) {
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' 404 Not Found');
echo '<h1>Not Found</h1>';
exit;
}
}
$p = $_GET['path'];
//has path
if (strlen($p)<=1)
NotFound();
$clean = explode('?', $p);
$clean = explode('#', $clean[0]);
$params = explode('/', substr($clean[0], 1)); //drop first /
//I use a check for two, because I dont allow images in the root folder
//I also use the path to determine how it should look
//EG: thumb/125/90/imagecode.jpg
if (count($params)<2)
NotFound();
$type = $params[0];
//I use the type to handle different methods. For this example I only used the full sized image
//You could use the same to handle thumbnails or cropped/watermarked
switch ($type) {
//case "crop":if (Crop($params)) return; else break;
//case "thumb":if (Thumb($params)) return; else break;
case "image":if (Image($params)) return; else break;
}
NotFound();
?>
<?php
/*
Just some example to show how you could create a responds
Since you already know how to create thumbs, I'm not going into details
Array
(
[0] => image
[1] => imagecode.JPG
)
*/
function Image($params) {
$tmp = explode('.', $params[1]);
if (count($tmp)!=2)
return false;
$code = $tmp[0];
//WARNING!! SQL INJECTION
//USE PROPER DB METHODS TO GET REALPATH, THIS IS JUST EXAMPLE
$query = "SELECT realpath FROM images WHERE Code='".$code."'";
//exec query here to $row
$realpath = $row['realpath'];
$f = file_get_contents($realpath);
if (strlen($f)<=0)
return false;
//create folder structure
#mkdir($params[0]);
//if you had more folders, continue creating the structure
//#mkdir($params[0].'/'.$params[1]);
//store the image, so a second request won't access this script
file_put_contents($params[0].'/'.$params[1], $f);
//you could directly optimize the image for web to make it even better
//optimizeImage($params[0].'/'.$params[1]);
//now serve the file to the browser, because even the first request needs to show the image
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $params[0].'/'.$params[1]));
echo $f;
return true;
}
?>
I would suggest you to create watermarked images on-the-fly and to cache them at the same time as everybody suggested.
Then you could create a garbage-collector PHP script that will be executed every days (using cron). This script will browse your cache folder to read every image access time. This can done using fileatime() PHP method. Then when a cached wm image has not been accessed within 24 or 48 hours, just delete it.
With this method, you can handle spike periods as images are cached at the first request. AND you will save your HDD space as your garbage-collector script will delete unused images for you.
This method will only work if your server partition has atime updates enabled.
See http://php.net/manual/en/function.fileatime.php
For most scenarios, lazily applying the watermark would probably make most sense (generate the watermarked image on the fly when requested then cache the result) however if you have big spikes in demand you are creating a mechanism to DOS yourself: create the watermarked version on upload.
Considering your HDD storage capacity and Pikes.
I would only create a watermarked image if it is viewed.(so yes on the fly) In that way you dont use to much space with a bunch a files that are or might not be viewed.
I would not watermark thumbnails i would rather make a filter that fake watermark and protect from being saved. That filter would apply to all thumbnails without creating a second image.
In this way all your thumbbails are watermarked (Fake with onther element on top).
Then if one of these thumbnails is viewed it generate a watermarked image (only once) since after its generated you load the new watermarked image.
This would be the most efficient way to deal with your HDD storage and Pikes.
The other option would be to upgrade your hosting services. Godaddy offer unlimited storage and bandwith for about 50$ a year.

Get remote image with PHP and show it with GD

I have a code in my website to show remote Gravatar portraits or uploaded images. Uploaded is ok, but i can't get the gravatar images.
Cant use file_get_contents because it´s not allowed on my host.
Heres the start check for the file
if(file_exists($arUser['imagem'][0])){
$imgPath = $arUser['imagem'][0]; //Usar a imagem enviada
}elseif(!strlen($arUser['imagem'][0]) && checkRemoteFile('http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150')){
$imgPath = 'http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150';
}else
$imgPath = '../img/social_noavatar_150.jpg'; //Temporario
So this doesn´t work:
$imgData = getimagesize($imgPath);
$src = imagecreatefromwhatever($imgPath);
I know I should replace:
$imgPath = 'http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150';
with something like:
$imgPath = GetFileData('http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150');
or
*$imgPath = file_get_contents('http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150');*
Got error with both and I can´t create the image:
I´ve searched for the answer but the others didn't fited to me.
Sorry for my bad english. :(
Note:
function imagecreatefromwhatever($image){
$info = pathinfo($image);
$extension = strtolower($info['extension']);
switch($extension) {
case "jpg":
return imagecreatefromjpeg($image);
break;
case "jpeg":
return imagecreatefromjpeg($image);
break;
case "png":
return imagecreatefrompng($image);
break;
case "gif":
return imagecreatefromgif($image);
break;
default:
return imagecreatefromjpeg($image);
}
}
Well, if file_get_content is not available on your host, you might be out of luck. If this is a security feature on your host then you won't find a single function that allows you to get data from another server.
You might want to simply bypass the function and set your image src url to the gravatar one. Something like :
<img src="http://www.gravatar.com/avatar/<?=md5($arUser['email'][0]);?>&fs=150" width="150"/>
(Please double check the url, I added a & before the fs as it makes more sense, but I don't know how gravatar api url looks like)
This way it is the client browser that will make the request and not your server.
The easiest way I have found is to transfer the location:
if(checkRemoteFile('http://www.gravatar.com/avatar/'.md5($arUser['email'][0])))
header('Location: http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs='.$sizePic);
If your PHP security on your server prevents remote grabbing of files via file_get_contents(), then your next best option is a CURL call to get the file contents fed in perhaps.
If your host doesn't allow file_get_contents it's likely other methods will not work. From my experience the hosts will prevent any external socket connections from script. So check with the host first.
Assuming that is the issue, you could still echo out an tag with the src attribute to the same location you're trying to get in script.
<img src="<?php echo('http://www.gravatar.com/avatar/'.md5($arUser['email'][0]).'fs=150');?>" />
You could also use the same trick to put the url into a style if you don't want to use the tag.

Store an image into DB

I use a script (http://www.webmotionuk.co.uk/php-jquery-image-upload-and-crop-v11/) that handles an image and then create a thumbnail.
Actually I don't want to store images on the file system but I want to store them in a mysql db in a BLOB field.
I could first store the created thumbnail on the fs, store the file in the DB and after i can delete the thumb from FS.
Is there a way to store directly the image on the DB?
This is the function:
function resizeThumbnailImage($thumb_image_name, $image, $width, $height, $start_width, $start_height, $scale){
list($imagewidth, $imageheight, $imageType) = getimagesize($image);
$imageType = image_type_to_mime_type($imageType);
$newImageWidth = ceil($width * $scale);
$newImageHeight = ceil($height * $scale);
$newImage = imagecreatetruecolor($newImageWidth,$newImageHeight);
switch($imageType) {
case "image/gif":
$source=imagecreatefromgif($image);
break;
case "image/pjpeg":
case "image/jpeg":
case "image/jpg":
$source=imagecreatefromjpeg($image);
break;
case "image/png":
case "image/x-png":
$source=imagecreatefrompng($image);
break;
}
imagecopyresampled($newImage,$source,0,0,$start_width,$start_height,$newImageWidth,$newImageHeight,$width,$height);
switch($imageType) {
case "image/gif":
imagegif($newImage,$thumb_image_name);
break;
case "image/pjpeg":
case "image/jpeg":
case "image/jpg":
imagejpeg($newImage,$thumb_image_name,90);
break;
case "image/png":
case "image/x-png":
imagepng($newImage,$thumb_image_name);
break;
}
chmod($thumb_image_name, 0777);
return $thumb_image_name;
}
How to:
Use file_get_contents() on the returned thumbnail, and then write the results to the database blob field.
$image = file_get_contents($thumbnail);
mysql_query("INSERT INTO tblName (fieldName) VALUES ('{$image}')");
But, you may not want to because:
That being said, I would encourage you to really consider what you're doing. Images pulled from the database won't be cached, from my understanding. Additionally, database connections will remain opened for much longer, using more resources. Also, your database size will grow rapidly, making it more laborious to manage migration down the road.
You can store binary data such as an image into a BLOB field in a database. It's as simple as putting the file contents into an INSERT statement. However, I highly suggest you take a look at some of these threads. Storing an image in a database is usually not the best way to go:
User Images: Database or filesystem storage?
Storing images in database: Yea or nay?
Should I store my images in the database or folders?
Would you store binary data in database or folders?
Store pictures as files or or the database for a web app?
Storing a small number of images: blob or fs?
store image in filesystem or database?
As J. Sampson said, it's not a good way to store images, but it is possible. Do as Jonathan said and then get data out of db, something similar to this:
<?php
$query = mysql_query("SELECT * FROM tbl WHERE id = 'blaa';");
$data = mysql_fetch_array($query);
//Then create image with function
imagecreatefromstring($data['imgdata'])
?>

Categories