Optimizing WordPress’s Full Size Images

Published by John on August 10, 2018 Under Wordpress

I work with a real estate company, who has a number of properties in their area that they list on their WordPress website using a custom post type called homes. They display a gallery of images on each home’s page, as well as a few other images like floor plans and a screenshot of Google Maps.

After a few months of using their site, they realized that the images on the home slideshow were very large and in an effort to optimize the site, wanted to reduce these images.

By default, WordPress creates a copy of an image that uses reduces quality for each thumbnail size and you can add as many different sizes as you want. However, the issue I was running into was that WordPress does not create a thumbnail if the image is smaller than the thumbnail size and it doesn’t resize the full size image. Nor, did the company want to touch the original full size image, as they liked having those on the website, so that they could use them in marketing and print materials if needed.

I looked at a few solutions, including the popular Smush plugin, but without a paid subscription, the full size image wouldn’t be touched. It did look like I could possibly force WordPress to resize the full size image, by either playing with cropping size or reducing the size slightly. However, in that case, the original image would get over-written, which was what they were trying to avoid. So, I came up with the below solution, which seems to work fairly well so far.

When an image is uploaded and the meta data processed, this code will create a copy of the image, with reduced quality. In the case of PNG images, it converts it to JPEG, so as to reduce size as much as possible.

In the most extreme example on this site, this tool the image size from 30 MB of slideshow images down to about 3 MB. In most cases though, it was more reasonable about 3MB down to .8 MB.

The Code

Use the below code at your own risk and with no express warranty. Please backup your site regularly and prior to installing or using the below! Since the below is creating an extra copy of each JPG and PNG image you upload, this will also increase your uploads folder size.


/* Image Optimization */

function kcr_wp_generate_attachment_metadata($metadata, $attachment_id){
	
	if(!is_array($metadata) || empty($metadata['file'])){
		return $metadata;
	}	
	
	$upload_dir = wp_upload_dir();
	
	$file_path = trailingslashit($upload_dir['basedir']).$metadata['file'];
	$save_path = trailingslashit($upload_dir['basedir']).'crunched/'.$metadata['file'];
	
	if(!file_exists($file_path)){
		return $metadata;
	}	
	
	$file_type = wp_check_filetype($file_path);
	
	if(!is_array($file_type) || empty($file_type['type']) || ($file_type['type'] != 'image/jpeg' && $file_type['type'] != 'image/png') || kcr_ends_with('.php', strtolower($file_path))){
		return $metadata;
	}
	
	$image = wp_get_image_editor($file_path);
	
	if ( is_wp_error( $image ) ) {
		return $metadata;
	}
	
	if($file_type['type'] == 'image/png'){
		$save_path = str_replace('.png', '.jpg', $save_path);
	}
	
	$image->set_quality(80);
	$image->save($save_path);

	return $metadata;

}

add_filter( 'wp_generate_attachment_metadata', 'kcr_wp_generate_attachment_metadata', 10, 2 );

function kcr_delete_attachment($attachment_id){
	
	$file_path = get_attached_file($attachment_id, true);
	
	if(empty($file_path)){
		return;
	}
		
	$file_type = wp_check_filetype($file_path);
	
	if(!is_array($file_type) || empty($file_type['type']) || ($file_type['type'] != 'image/jpeg' && $file_type['type'] != 'image/png') || kcr_ends_with('.php', strtolower($file_path))){
		return;
	}
	
	$optimized_path = str_replace('/wp-content/uploads/', '/wp-content/uploads/crunched/', $file_path);
	$real_optimized_path = realpath($optimized_path);
	$upload_dir = wp_upload_dir();	
	
	if(is_dir($optimized_path) || $real_optimized_path != $optimized_path || !kcr_starts_with($upload_dir['basedir'], $optimized_path)){
		return;
	}
	
	
	if(!file_exists($optimized_path)){
		return;
	}
	
	@unlink($optimized_path);	
	
}

add_action ('delete_attachment', 'kcr_delete_attachment', 10, 1);

function kcr_ends_with($needle, $haystack){
	
	if(empty($needle) || empty($haystack)){
		return false;
	}
	
	$needle_size = strlen($needle);
	$haystack_size = strlen($haystack);
	
	if($needle_size > $haystack_size){
		return false;
	}
	
	if(substr($haystack, ($haystack_size - $needle_size)) == $needle){
		return true;
	}
	
	return false;	
	
}

function kcr_starts_with($needle, $haystack){
	
	if(empty($needle) || empty($haystack)){
		return false;
	}
	
	$needle_size = strlen($needle);
	$haystack_size = strlen($haystack);
	
	if($needle_size > $haystack_size){
		return false;
	}
	
	if(substr($haystack, 0, $needle_size) == $needle){
		return true;
	}
	
	return false;	
	
}

function kcr_get_optimized_image($image_url, $site_url=''){
	
	$original_image_url = $image_url;
	
	if(empty($site_url)){
		$site_url = site_url();
	}
	
	if(!kcr_ends_with('/', $site_url)){
		$site_url .= '/';
	}
	
	if(kcr_ends_with('.png', $image_url)){
		$image_url = str_replace('.png', '.jpg', $image_url);
	}
				
				
	$image_url = str_replace('/wp-content/uploads/', '/wp-content/uploads/crunched/', $image_url);
	$image_path = str_replace($site_url, ABSPATH, $image_url);
	
	if(!file_exists($image_path)){
		$image_url = $original_image_url;
	}
	
	return $image_url;	
	
}

/* Image Optimization */

How it Works

The above kcr_wp_generate_attachment_metadata function hooks into WordPress’s wp_generate_attachment_metadata filter, which gets called after an image has been uploaded, but not saved in database. It creates a copy of the image in a under /wp-content/uploads/crunched/ using the same folder structure and name as the original image.

When deleting an attachment, the kcr_delete_attachment function hooks into the delete_attachment WordPress Action, runs a couple checks and if the optimized image is present, removes it.

After installing the above in your functions.php file, you would need to regenerate thumbnails using a plugin to prime the crunched folder.

Within your theme, if you want to use the optimized image, you can do so as follows:


$image_url = get_image_url_somehow();
$site_url = site_url().'/';

$image_url = kcr_get_optimized_image($image_url, $site_url);

echo $image_url;

In the above example, $site_url is optional, but if you are doing a bunch of images on the same page, you might save a bit of overhead by setting it as a variable.

While the above is set to hook into the wp_generate_attachment_metadata filter, you could also lazily create these by doing the following(just make sure to use the path of the file and NOT the URL:


$metadata = array('file'=>THE_FILE_PATH);
kcr_wp_generate_attachment_metadata($metadata, 1);


No Comments |

Add a Comment