I am using Glide to deliver image content from one of my sites. This is working well and I have now built a file upload so that admins can upload images to the site for subsequent download.
Some of the images that admins will upload will be much larger than I need (or want the overhead of storing on the server), so I want to downsize them, preferably during the upload routine or failing that, just after they have been saved to their new location (storage/app/images)
So, I've been hacking around with intervention for instance without much success because of my poor understanding of the file names and paths available from getClientOriginalName/Extension etc.
Could anyone show me a pattern for this which would work well. Ideally I'd love to include something like I've seen on others' examples like...
$img = Image::make('foo.jpg')->resize(300, 200);
... in the correct place in my code
foreach($files as $file) {
$fileExtension = $file->getClientOriginalExtension();
$fileMimeType = $file->getMimeType();
if(in_array($fileExtension, $allowableExtensions)) {
if(in_array($fileMimeType, $allowableMimes)) {
array_push($dbFileList, $file->getClientOriginalName());
$newImage = '/images/' . $propertyCode . '/' . $file->getClientOriginalName();
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
}else{
$errorMessage = 'At least one file was not an image, check your results...';
}
}else{
$errorMessage = 'At least one file was not an image, check your results...';
}
}
Update 1:
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
$img = Image::make($file);
Storage::put('/images/new/' . $file->getClientOriginalName(), $img);
This updated code outputs the files to the /new directory and all looks fine, but the output files have 'zero bytes'. What am I missing?
Update 2: Final code
The final answer (after using the proper code provided by contributors) was that:
I had to move my app from virtual box on to the dev machine (iMac) to prevent extra confusion with paths
The path for the images must exist prior to making the ->save()
The path variable must be set in advance of the ->save()
I don't need the Storage::put at all, so the larger file never ends up on the server.
Then this final code started to work.
$path = storage_path('app/smallpics/')."/".$file->getClientOriginalName();
$img = Image::make($file)->resize(300,200)->save($path);
Much thanks to all of you. You make my Laravel learning curve a bit less terrifiying!!
You can use Intervention to manipulate your image (resize etc.) as
$new_image = Image::make($file)->resize(300,200)->save('/path/to/save');
The image upload and resize work flow is like:
Upload the image from tmp to your directory.
Make a copy of that image by setting the height, width, quality and save it in the same or some other directory.
Delete the original image.
So as per your code flow:
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
after this code, put the image compress code and after that delete the original image.
you can use Intervention or just use imagemagick convert command line command for resize or convert.
Pay attention to comments :
public function saveUploadPic(Request $request)
{
$pic = $request->file('<NAME_OF_FILE_INPUT_IN_HTML_FORM>');
#check for upload correctly
if(!$pic->isValid())
{
throw new Exception("IMAGE NOT UPLOADED CORRECTLY");
}
#check for mime type and extention
$ext = $pic->getClientOriginalExtension();
$mime = $pic->getMimeType();
if(!in_array($mime, $allowedMimeTypeArray) || !in_array($ext, $allowedExtArray))
{
throw new Exception("This Image Not Support");
}
#check for size
$size = $pic->getClientSize() / 1024 / 1024;
if($size > $allowedSize)
{
throw new Exception("Size Of Image Is More Than Support Size");
}
########################YOU HAVE TWO OPTION HERE###################
#1- save image in a temporary location with random hash for name if u need orginal image for other process
#below code save image in <LARAVEL_APP_PATH>/storage/app/tmp/pics/
$hash = md5(date("YmdHis").rand(1,10000));
$pic->storeAs('tmp/pics', $hash.'.'.$ext);
#Then resize or convert it
$img = Image::make(storage_path('app/tmp/pics/'.$hash.'.'.$ext))->resize(300, 200);
#save new image whatever u want
$img->save('<PATH_TO_SAVE_IMAGE>');
#after u finish with orginal image delete it
Storage::delete(storage_path('app/tmp/pics/'.$hash.'.'.$ext);
#2- Or just use below for resize and save image witout need to save in temporary location
$img = Image::make($pic->getRealPath())->resize(300,200);
$img->save('<PATH_TO_SAVE_IMAGE>');
}
if you want to use convert see this link.
So I'm following the example given here (which I modified to only blur, no watermark), to make a blurred image in WordPress on upload. The problem is, that if the uploaded file is the exact same size, or smaller, than the set size, then WordPress will not generate an image, and hence no blurred one will be made.
I tried using a isst($meta['sizes']['background-image-blurred']['file']) to determine if one was made, and if not then copy() the source file, but then no WordPress "metadata" would be generated for the image (for non-WordPress people, the metadata is different than what you think), so it would give height/width undefined problems when displaying using wp_get_attachment_image.
So I'm convinced using wp_get_attachment_image hook as shown below is probably the wrong way to do this. It probably needs to happen earlier in the image upload process.
Any ideas on how to best get this working?
/**
* Several functions relatting to blurring images on uploaded.
* #see https://codeable.io/community/how-to-watermark-wordpress-images-with-imagemagick/
*/
add_image_size( 'background-image-blurred', 1920, 1080, true );
function generate_blurred_image( $meta ) {
$time = substr( $meta['file'], 0, 7); // Extract the date in form "2015/04"
$upload_dir = wp_upload_dir( $time ); // Get the "proper" upload dir
$filename = $meta['sizes']['background-image-blurred']['file'];
$meta['sizes']['background-image-blurred']['file'] = blur_image( $filename, $upload_dir );
return $meta;
}
add_filter( 'wp_generate_attachment_metadata', 'generate_blurred_image' );
function blur_image( $filename, $upload_dir ) {
$original_image_path = trailingslashit( $upload_dir['path'] ) . $filename;
$image_resource = new Imagick( $original_image_path );
$image_resource->gaussianBlurImage( 10, 100 ); // See: http://phpimagick.com/Imagick/gaussianBlurImage
return save_blurred_image( $image_resource, $original_image_path );
}
function save_blurred_image( $image_resource, $original_image_path ) {
$image_data = pathinfo( $original_image_path );
$new_filename = $image_data['filename'] . '-blurred.' . $image_data['extension'];
// Build path to new blurred image
$blurred_image_path = str_replace($image_data['basename'], $new_filename, $original_image_path);
if ( ! $image_resource->writeImage( $blurred_image_path ) ) {
return $image_data['basename'];
}
// Delete the placeholder image WordPress made now that it's been blurred
unlink( $original_image_path );
return $new_filename;
}
Unfortunately wp hasn't got a filter to force a size so what you can do is hook in after and resize your image if not created and pop it into the metadata.
I haven't got imagick so you will have to try these functions yourself, but you have the correct process above, you just need to update the filename and type in the array below. PS don't output anything within the filter!
function custom_img_size(){
add_image_size( 'background-image-blurred', 1920, 1080, true );
}
add_action( 'after_setup_theme', 'custom_img_size' );
add_filter('wp_generate_attachment_metadata', 'force_add_size', 100);
function force_add_size( $metadata ) {
if(!isset($metadata['sizes']['background-image-blurred'])){
//not set so initiate our custom size...
//I dont have imagick installed so just use your functions here to duplicate
//note original file = $filename update the $newfilename below...
//sample resize code ...
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-1200x1080', $filename).$extension;
copy($filename, $newfilename );
//end sample resize code.....
$filetype= 'image/jpeg';
$metadata['sizes']['background-image-blurred']= array(
"file"=> $newfilename,
"width"=> 1920,
"height"=> 1080,
"mime-type"=> $filetype
);
}
return $metadata;
}
Updates
This is designed to only catch where your existing filter has failed to create your blurred custom size otherwise it does nothing. You should still include your original filters. You may have an issue in the original code: You are deleting the original file in your filters and this will cause issues as there is a postmeta field called '_wp_attached_file' that will need updating. I have included a note below on this.
The filter catches the metadata before saving so any changes are also going to be saved once you return the $metadata. If you look at the source code: https://core.trac.wordpress.org/browser/tags/3.8.1/src/wp-admin/includes/image.php#L72 here you can see exactly how it works. I've also confirmed using wp4.3
I have attempted to insert the imagick functions you need below. I havent tested as i dont actually have it installed anywhere. (imagemagick is actually a wonderful opensource program but very server intensive). Try this function in place of the one above:
add_filter('wp_generate_attachment_metadata', 'force_add_size', 100, 2);
function force_add_size( $metadata, $id ){
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-blurred', $filename).$extension;
$image_resource = new Imagick( $filename);
$image_resource->resizeImage(1920,1080,Imagick::FILTER_LANCZOS,.3);
$image_resource->writeImage( $newfilename );
//http://www.dylanbeattie.net/magick/filters/result.html
unlink( $filename );//delete original image altogether ---> you might want to update the post meta on this '_wp_attached_file' , you can actually get the attachment id from the filter, i have added it above. It would be best to have a actual image url in there! something like $sfile= str_replace($upload_dir['basedir'],'', $newfilename); update_post_meta($id, '_wp_attached_file', $sfile );
switch($extension){
case '.jpg':
case '.jpeg':
$type = 'image/jpeg';
break;
case '.gif':
$type = 'image/gif';
break;
case '.png':
$type = 'image/png';
break;
default:
$type = 'image/jpeg'; // you might want a conditional to check its actually a image...imagick will do this for you as well....it shouldnt get this far if not a image.
break;
}
$metadata['sizes']['background-image-blurred']= array(
"file"=> $newfilename,
"width"=> 1920,//your custom image size, has to be this! you could get the global var and check for sizes but its this size in particular we want?
"height"=> 1080,
"mime-type"=> $type
);
return $metadata;
}
update
to prevent the image stretching out smaller images replace the imagick code with this.
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-blurred', $filename).$extension;
$image_resource = new Imagick( $filename);
if($image_resource->getImageWidth() <= 1920 || $image_resource->getImageHeight() > <= 1020) {
return $metadata;
}
$image_resource->resizeImage(1920,1080,Imagick::FILTER_LANCZOS,.3);
$image_resource->writeImage( $newfilename );
//http://www.dylanbeattie.net/magick/filters/result.html
When I was first reading your question I was unsure if you were having problems setting the image or generating the image or both. I thought the issue with setting the image might be that you needed to update the post image with wp_update_attachment_metadata after using wp_generate_attachment_metadata to have the post use the new image instead.
When I reread your question I realized it had to be an issue with the add_image_size() because it happens with an image the same size or smaller than the one uploaded. I had once run into this issue as well with (re)generating an alternate size image for a new theme. In my case it was that either the width or the height parameter was not being met on add_image_size().
I wanted to verify this in the Wordpress Code Reference and found a comment by a contributor near the bottom that is exactly the same issue you face, albeit without a solution posted.
If you upload an image whose dimensions match the add_image_size()
when crop is set to true, in the $meta object accessed by the
wp_generate_attachment_metadata filter, that matching image size will
not be available. Also, image sizes that have larger dimensions than
an uploaded photo will not be available either.
(Thus if you are using a technique to create something like a
monochrome derivative image, you won’t be able to get it to work if
the uploaded image is exactly the same size as the image size you’re
using for your black and white version).
I think in your case there is a workaround solution since you are using Imagick, in the fact that you can do some image math. Using the borderImage function simply add a border to each image up to 1 pixel larger than the image you have uploaded. Since the add_image_size() crops from center by default that should trigger the resize to the size you need and solve the issue.
For example you want dimensions of 1920 x 1080. Using Imagick getSize you can check the size of the image. Lets say it is exactly 1920 x 1080, but as we know this will not trigger the blur. So using borderImage we add a 1 pixel white border to the image. This should trigger wordpress to resize the image to 1920 x 1080 as long as crop is set to true.
If the image is smaller but relatively close to the needed size we just make the border lets say 10 pixels and so on to fill in the size, and let wordpress crop from center. This will work with proportional images only.
If the image is a good bit smaller, lets say 200 x 800, we should consider adding a different add_image_size() option to handle smaller images. If you need to forge ahead then we can either use Imagick to "expand" our image to the needed proportions OR we can create a new white Imagick canvas the size we need and overlay the image to top left such that we have whitespace below and to the right of our new image. You would then use the add_image_size crop parameter to cutoff the excess white. For example, add_image_size('background-image-blurred', 1920, 1080, array('left','top')) to set the crop starting from the top left of the image.
i think you need to trick a bit why you are not trying to have image of the size 1 x 1 or 5 X 5 so every time regardless what ever the image would be it will generate the Thumbnail for that image.
and upon the 'wp_generate_attachment_metadata' Hack the Stuff and generate the image from the original image and replace the 1 x 1 image with the Blured image which you want there off cource you need to trick over the filter in case you are using 'background-image-blurred' at any places for display or other computation purpose.
Hope you got the idea i think it's a little bit hack but should work proper.
What about focusing on the wp_handle_upload() function?
Here is an example of how it was used to blur images.
An outside-the-box suggestion: since you are only blurring the image and not making any other changes to the image, why not let CSS do the legwork instead of creating another image on the server?
.heroBackgroundImage {
background: url('uploadedimage.jpg');
-webkit-filter: blur(8px);
-moz-filter: blur(8px);
-o-filter: blur(8px);
-ms-filter: blur(8px);
filter: blur(8px);
-webkit-transform: translate3d(0,0,0);
}
Save the server effort; let it be handled on the client side.
EDIT: Just saw your comment about it can't be CSS. Added webkit-transform to move effect to the GPU, making it render more efficiently. Otherwise, still saving for posterity.
The example you are referring to makes use of wordpress filters.
The problem is: A wordpress filter is always bound to a certain image resolution and therefore you cannot have the image processed regardless of its resolution.
My recommendation is that you avoid filters. Instead pre-process the image at an earlier point in time. Please find below a sample code that will blur the image initially before handing over control to wordpress:
// perform new upload
include_once( ABSPATH . 'wp-admin/includes/image.php' );
$imagetype = end(explode('/', getimagesize($imageurl)['mime']));
$uniq_name = date('dmY').''.(int) microtime(true);
$filename = $uniq_name.'.'.$imagetype;
$uploaddir = wp_upload_dir();
$uploadfile = $uploaddir['path'] . '/' . $filename;
$contents= file_get_contents($imageurl);
$savefile = fopen($uploadfile, 'w');
fwrite($savefile, $contents);
fclose($savefile);
// apply blur
$image_resource = new Imagick( $uploadfile );
$image_resource->resizeImage(500, 500, null, 1);
$image_resource->blurImage( 40, 20 );
$image_data = pathinfo( $uploadfile );
$new_filename = $image_data['filename'] . '-blur.' . $image_data['extension'];
$blur_image_path = str_replace($image_data['basename'], $new_filename, $uploadfile);
if ( ! $image_resource->writeImage( $blur_image_path ) ) {
unlink( $uploadfile );
return ""; // fixme
}
unlink( $uploadfile );
$wp_filetype = wp_check_filetype(basename($filename), null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => $imageurlHash,
'post_content' => '',
'post_status' => 'inherit'
);
$attach_id = wp_insert_attachment( $attachment, $blur_image_path );
$imagenew = get_post( $attach_id );
$fullsizepath = get_attached_file( $imagenew->ID );
$attach_data = wp_generate_attachment_metadata( $attach_id, $fullsizepath );
wp_update_attachment_metadata( $attach_id, $attach_data );
From an Android app I post a photo into a webserver. Inside the PHP script I want to save the file inside a directory :
$name = "parcelle_" . $photo->lireBienPhotoSeq() . ".png";
if ( ! file_exists (RP_PHOTO_PARCELLE) ) {
mkdir(RP_PHOTO_PARCELLE, 0777, true);
chmod(RP_PHOTO_PARCELLE, 0777);
}
$dest = DIR_MAIN . '/data/photo_parcelle/' . $name;
$image = base64_decode($_POST["image"]);
$img = imagecreatefromstring($image);
if ($img !== false)
imagepng($img, $dest);
$chemin = PHOTO_PARCELLE . $name;
The problem is that the image is not created and the Android app receives a response code 500 ( HTTP_INTERNAL_ERROR ) ! I made a ls -l to see the rights of the directory and it is 0777. So what is wrong ?
There are several things that could be going wrong on this one, and your description is not detailed enough.
How is the image POSTed ?
Are you sure it is sent as a POST variable and not as an uploaded file in $_FILES ?
Are you sure that $_POST['image'] contains the contents of a recognizable image format (JPEG, PNG, GIF, WBMP, and GD2) and not something like raw data ?
Are you sure that RP_PHOTO_PARCELLE is the same as DIR_MAIN . '/data/photo_parcelle/' ?
What is the result of imagecreatefromstring($image); ?
If you directly invoke the script do you get it to execute or a 500 error ?
After looking at the log then the imagecreatefromstring function is unknown , so the gd package is installed and it works ! The server is new and not as my local computer ! lol
I am currently creating a component, where a user creates a new image and there's an upload button and a name input, all the basic gallery fields for an Image..
But I have come across two issues, the one is how would i write the following code so that it exports one image b/w and the other color scaled to amax width of 160, Here's the code I have:
function save(){
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
$input=JFactory::getApplication()->input;
$input->get('jform', NULL, NULL);
$file = JRequest::getVar('jform', null, 'files', 'array');
$data = JRequest::getVar( 'jform', null, 'post', 'array' );
$path = JPATH_ROOT;
//
//
// Make the file name safe.
jimport('joomla.filesystem.file');
$file['name']['logo'] = JFile::makeSafe($file['name']['logo']);
// Move the uploaded file into a permanent location.
if (isset($file['name']['logo'])) {
// Make sure that the full file path is safe.
$filepath = JPath::clean($path. DS ."images". DS ."associations". DS . strtolower($file['name']['logo']));
// Move the uploaded file.
JFile::upload( $file['tmp_name']['logo'], $filepath );
$data['logo'] = strtolower( $file['name']['logo'] );
//convert image
$image = $filepath;
$im = new Imagick();
$im->pingImage($image);
$im->readImage( $image );
$im->setImageResolution(72,72);
$im->resampleImage(72,72,imagick::FILTER_UNDEFINED,1);
$im->scaleImage(160,0);
$im->setImageFormat('jpeg');
$im->setImageCompression(imagick::COMPRESSION_JPEG);
$im->setImageCompressionQuality(60);
$im->modulateImage(100, 0, 100);
$im->writeImage($image);
$im->destroy();
}
$input->post->set('jform',$data);
return parent::save();
}
And Finally, How Would I manage this? it saves the image name in the database but once i return to this item it just has an upload field, where it would be rather useful to have the image name or the actual image with a delete function showing and a reupload...
Am I going the right way about this?... Any Help Greatly Appreciated... Thank you :)
How to produce 2 images
You have to create 2 resources out from the uploaded original, transform each and save in the separate file. For example I'be using the JImage package (note there's no limitcolors filter available)
$image_one = new JImage($filepath);
$image_one
->filter('grayscale')
->toFile($path_one, IMAGETYPE_JPEG, array('quality' => 60));
$image_two = new JImage($filePath);
$image_two
->filter('limitcolors', array('limit' => 160))
->toFile($path_two, IMAGETYPE_GIF);
Managing images
Depends on the reason, why there"s functionality being implemented and so relation of the uploaded image to other objects/ events.
If the uploaded images are used for user profiles, you should store a user id in the database along with image location. Next time check if current users has any images related to his/ her account and display.
I'm having trouble to make a php file that'll be able to copy remote images and save it to a directory.
so i'm going to input a set of image urls into the first text box, and when run the image will grab those images and save them to a specified directory and display the new set of urls. :)
How can i do that?
Place this code in your script.php
<?php
$image = 'http://remote.com/image.jpeg';
file_put_contents(dirname(__FILE__).'/'.basename($image), file_get_contents('http://remote.com/image.jpeg'));
This should start you off
header("Content-Type: image/jpeg");
echo file_get_contents("http://www.somewebsite.com/images/image.jpg");
You could then save this file to a local directory
I'd start by using file_get_contents() along with the fopen(), fwrite(), and fclose() gang.
<?php
foreach( $_POST['image'] as $image ) {
$f = fopen( "/path/to/image/folder/".basename( $image ) ); //Open image file
fwrite( $f, file_get_contents( $image ) ); //Write the contents of the web image to the newly created image on your server
fclose( $f ); //Close the file
}
?>