Make WordPress Core

Opened 14 years ago

Last modified 4 years ago

#15311 new enhancement

dynamic image resize (on the fly) using already available functions

Reported by: vteixeira's profile vteixeira Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 3.1
Component: Media Keywords: dev-feedback has-patch
Focuses: Cc:

Description

The lack of a dynamic resize function in WordPress forces theme developers to register lots of image sizes for their themes to use.

One of the problems with this approach is that the server becomes full of image files that will be never used.

Another problem is that when someone changes their theme the image sizes simply doesn't match, forcing people to use a plugin to regenerate all image files, and once again lots of those files will never be used.

So theme developers right now are using some sort of image resizing script like timthumb that works outside of wp. I think it has many drawbacks comparing to a native implementation.

So I made a function that uses WordPress native image handling capabilities to resize and save those resized images for future use.

I use this for attached images as well as standalone files such as custom fields and other images.

What I want here is just to share my solution, and maybe we can someday put something like this into core (actually something better then this):

/*
 * Resize images dynamically using wp built in functions
 * Victor Teixeira
 *
 * php 5.2+
 *
 * Exemple use:
 * 
 * <?php 
 * $thumb = get_post_thumbnail_id(); 
 * $image = vt_resize( $thumb,'' , 140, 110, true );
 * ?>
 * <img src="<?php echo $image[url]; ?>" width="<?php echo $image[width]; ?>" height="<?php echo $image[height]; ?>" />
 *
 * @param int $attach_id
 * @param string $img_url
 * @param int $width
 * @param int $height
 * @param bool $crop
 * @return array
 */
function vt_resize( $attach_id = null, $img_url = null, $width, $height, $crop = false ) {

	// this is an attachment, so we have the ID
	if ( $attach_id ) {
	
		$image_src = wp_get_attachment_image_src( $attach_id, 'full' );
		$file_path = get_attached_file( $attach_id );
	
	// this is not an attachment, let's use the image url
	} else if ( $img_url ) {
		
		$file_path = parse_url( $img_url );
		$file_path = ltrim( $file_path['path'], '/' );
		//$file_path = rtrim( ABSPATH, '/' ).$file_path['path'];
		
		$orig_size = getimagesize( $file_path );
		
		$image_src[0] = $img_url;
		$image_src[1] = $orig_size[0];
		$image_src[2] = $orig_size[1];
	}
	
	$file_info = pathinfo( $file_path );
	$extension = '.'. $file_info['extension'];

	// the image path without the extension
	$no_ext_path = $file_info['dirname'].'/'.$file_info['filename'];

	$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension;

	// checking if the file size is larger than the target size
	// if it is smaller or the same size, stop right here and return
	if ( $image_src[1] > $width || $image_src[2] > $height ) {

		// the file is larger, check if the resized version already exists (for crop = true but will also work for crop = false if the sizes match)
		if ( file_exists( $cropped_img_path ) ) {

			$cropped_img_url = str_replace( basename( $image_src[0] ), basename( $cropped_img_path ), $image_src[0] );
			
			$vt_image = array (
				'url' => $cropped_img_url,
				'width' => $width,
				'height' => $height
			);
			
			return $vt_image;
		}

		// crop = false
		if ( $crop == false ) {
		
			// calculate the size proportionaly
			$proportional_size = wp_constrain_dimensions( $image_src[1], $image_src[2], $width, $height );
			$resized_img_path = $no_ext_path.'-'.$proportional_size[0].'x'.$proportional_size[1].$extension;			

			// checking if the file already exists
			if ( file_exists( $resized_img_path ) ) {
			
				$resized_img_url = str_replace( basename( $image_src[0] ), basename( $resized_img_path ), $image_src[0] );

				$vt_image = array (
					'url' => $resized_img_url,
					'width' => $new_img_size[0],
					'height' => $new_img_size[1]
				);
				
				return $vt_image;
			}
		}

		// no cached files - let's finally resize it
		$new_img_path = image_resize( $file_path, $width, $height, $crop );
		$new_img_size = getimagesize( $new_img_path );
		$new_img = str_replace( basename( $image_src[0] ), basename( $new_img_path ), $image_src[0] );

		// resized output
		$vt_image = array (
			'url' => $new_img,
			'width' => $new_img_size[0],
			'height' => $new_img_size[1]
		);
		
		return $vt_image;
	}

	// default output - without resizing
	$vt_image = array (
		'url' => $image_src[0],
		'width' => $image_src[1],
		'height' => $image_src[2]
	);
	
	return $vt_image;
}

Attachments (9)

vt_resize.txt (3.5 KB) - added by daltonrooney 12 years ago.
Small update to predict final image size before naming it.
vt_resize.2.txt (3.9 KB) - added by foxinni 12 years ago.
Resize script to support MultiSite
15311.diff (14.8 KB) - added by prettyboymp 12 years ago.
Patch piggy backs off of 22100.2.diff in ticket @22100
15311.2.diff (5.2 KB) - added by ericlewis 9 years ago.
15311.3.diff (2.5 KB) - added by ericlewis 9 years ago.
15311.4.diff (2.5 KB) - added by ericlewis 9 years ago.
15311.5.diff (2.5 KB) - added by ericlewis 9 years ago.
15311.6.diff (4.4 KB) - added by ericlewis 9 years ago.
15311.7.diff (4.3 KB) - added by ericlewis 9 years ago.

Download all attachments as: .zip

Change History (141)

#1 @filosofo
14 years ago

For reference, here's a plugin I wrote to do something similar.

#2 @vteixeira
14 years ago

Hi Filosofo, I didn't know your plugin.

This function started as a less than 10 lines script, but soon I realized that I needed more checks before resizing the image.
And then I added the ability to resize any image even if it was not an attachment - this is really important because of the way we are using custom post types today with all those custom fields and metaboxes.
And I also wanted it to be available for plugins that uploads images to their own folders (and tables) as well.

What I keep asking myself is why we still don't have this in core.
All the functionality is already built in, it just needs to be made available as a simple function or a class.

The fact is every framework or cms out there have a dynamic resize function, we need this for wp.

The add_image_size() function really doesn't scales.

I'm convinced that the functions.php file (as I'm doing right now) is not the best place for this.

Maybe you or other dev could refactor this for a core function. It really needs some more testing as I don't know if it works without problems on a subfolder install.

#3 @Utkarsh
14 years ago

  • Cc admin@… added

#4 @vteixeira
14 years ago

  • Cc victorhteixeira@… added

#5 @anointed
14 years ago

  • Cc anointed added

fantastic script, using it already.. saved my bacon lol..

suggestion:
ability to control the 'quality' of the resized image itself for further optimization.

#6 @vteixeira
14 years ago

Quality control added!

@anointed: glad you liked it. It saves me everyday.

/*
 * Resize images dynamically using wp built in functions
 * Victor Teixeira
 *
 * php 5.2+
 *
 * Exemple use:
 * 
 * <?php 
 * $thumb = get_post_thumbnail_id(); 
 * $image = vt_resize( $thumb,'' , 140, 110, true, 70 );
 * ?>
 * <img src="<?php echo $image[url]; ?>" width="<?php echo $image[width]; ?>" height="<?php echo $image[height]; ?>" />
 *
 * @param int $attach_id
 * @param string $img_url
 * @param int $width
 * @param int $height
 * @param bool $crop
 * @param int $jpeg_quality
 * @return array
 */
function vt_resize( $attach_id = null, $img_url = null, $width, $height, $crop = false, $jpeg_quality = 90 ) {

	// this is an attachment, so we have the ID
	if ( $attach_id ) {
	
		$image_src = wp_get_attachment_image_src( $attach_id, 'full' );
		$file_path = get_attached_file( $attach_id );
	
	// this is not an attachment, let's use the image url
	} else if ( $img_url ) {
		
		$file_path = parse_url( $img_url );
		$file_path = ltrim( $file_path['path'], '/' );
		//$file_path = rtrim( ABSPATH, '/' ).$file_path['path'];
		
		$orig_size = getimagesize( $file_path );
		
		$image_src[0] = $img_url;
		$image_src[1] = $orig_size[0];
		$image_src[2] = $orig_size[1];
	}
	
	$file_info = pathinfo( $file_path );
	$extension = '.'. $file_info['extension'];

	// the image path without the extension
	$no_ext_path = $file_info['dirname'].'/'.$file_info['filename'];

	$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension;

	// checking if the file size is larger than the target size
	// if it is smaller or the same size, stop right here and return
	if ( $image_src[1] > $width || $image_src[2] > $height ) {

		// the file is larger, check if the resized version already exists (for crop = true but will also work for crop = false if the sizes match)
		if ( file_exists( $cropped_img_path ) ) {

			$cropped_img_url = str_replace( basename( $image_src[0] ), basename( $cropped_img_path ), $image_src[0] );
			
			$vt_image = array (
				'url' => $cropped_img_url,
				'width' => $width,
				'height' => $height
			);
			
			return $vt_image;
		}

		// crop = false
		if ( $crop == false ) {
		
			// calculate the size proportionaly
			$proportional_size = wp_constrain_dimensions( $image_src[1], $image_src[2], $width, $height );
			$resized_img_path = $no_ext_path.'-'.$proportional_size[0].'x'.$proportional_size[1].$extension;			

			// checking if the file already exists
			if ( file_exists( $resized_img_path ) ) {
			
				$resized_img_url = str_replace( basename( $image_src[0] ), basename( $resized_img_path ), $image_src[0] );

				$vt_image = array (
					'url' => $resized_img_url,
					'width' => $new_img_size[0],
					'height' => $new_img_size[1]
				);
				
				return $vt_image;
			}
		}

		// no cached files - let's finally resize it
		$new_img_path = image_resize( $file_path, $width, $height, $crop, $jpeg_quality );
		$new_img_size = getimagesize( $new_img_path );
		$new_img = str_replace( basename( $image_src[0] ), basename( $new_img_path ), $image_src[0] );

		// resized output
		$vt_image = array (
			'url' => $new_img,
			'width' => $new_img_size[0],
			'height' => $new_img_size[1]
		);
		
		return $vt_image;
	}

	// default output - without resizing
	$vt_image = array (
		'url' => $image_src[0],
		'width' => $image_src[1],
		'height' => $image_src[2]
	);
	
	return $vt_image;
}

#7 @anointed
14 years ago

finally... goodbye timthumb, was a nice ride, but luvin this so much better. Time to redo entire theme image replacement.

tested out quality control, works great!

#8 @anointed
14 years ago

something you might check into:

I've noticed that many .jpg images that I am uploading are for some reason all sizing to approx 300x wide even though I have it set in my theme to be 800x550. There seems to be nothing in common between those that resize properly and those that don't.

If need be, I can post a zip file of images that all seem to resize wrong if it helps.

#9 @vteixeira
14 years ago

I just found a little bug, but that had nothing to do with the image resizing. Actually the resizing is performed by the WordPress native function resize_image().

Anyway, check this one:

/*
 * Resize images dynamically using wp built in functions
 * Victor Teixeira
 *
 * php 5.2+
 *
 * Exemple use:
 * 
 * <?php 
 * $thumb = get_post_thumbnail_id(); 
 * $image = vt_resize( $thumb,'' , 140, 110, true, 70 );
 * ?>
 * <img src="<?php echo $image[url]; ?>" width="<?php echo $image[width]; ?>" height="<?php echo $image[height]; ?>" />
 *
 * @param int $attach_id
 * @param string $img_url
 * @param int $width
 * @param int $height
 * @param bool $crop
 * @param int $jpeg_quality
 * @return array
 */
function vt_resize( $attach_id = null, $img_url = null, $width, $height, $crop = false, $jpeg_quality = 90 ) {

	// this is an attachment, so we have the ID
	if ( $attach_id ) {
	
		$image_src = wp_get_attachment_image_src( $attach_id, 'full' );
		$file_path = get_attached_file( $attach_id );
	
	// this is not an attachment, let's use the image url
	} else if ( $img_url ) {
		
		$file_path = parse_url( $img_url );
		$file_path = ltrim( $file_path['path'], '/' );
		//$file_path = rtrim( ABSPATH, '/' ).$file_path['path'];
		
		$orig_size = getimagesize( $file_path );
		
		$image_src[0] = $img_url;
		$image_src[1] = $orig_size[0];
		$image_src[2] = $orig_size[1];
	}
	
	$file_info = pathinfo( $file_path );
	$extension = '.'. $file_info['extension'];

	// the image path without the extension
	$no_ext_path = $file_info['dirname'].'/'.$file_info['filename'];

	$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension;

	// checking if the file size is larger than the target size
	// if it is smaller or the same size, stop right here and return
	if ( $image_src[1] > $width || $image_src[2] > $height ) {

		// the file is larger, check if the resized version already exists (for crop = true but will also work for crop = false if the sizes match)
		if ( file_exists( $cropped_img_path ) ) {

			$cropped_img_url = str_replace( basename( $image_src[0] ), basename( $cropped_img_path ), $image_src[0] );
			
			$vt_image = array (
				'url' => $cropped_img_url,
				'width' => $width,
				'height' => $height
			);
			
			return $vt_image;
		}

		// crop = false
		if ( $crop == false ) {
		
			// calculate the size proportionaly
			$proportional_size = wp_constrain_dimensions( $image_src[1], $image_src[2], $width, $height );
			$resized_img_path = $no_ext_path.'-'.$proportional_size[0].'x'.$proportional_size[1].$extension;			

			// checking if the file already exists
			if ( file_exists( $resized_img_path ) ) {
			
				$resized_img_url = str_replace( basename( $image_src[0] ), basename( $resized_img_path ), $image_src[0] );

				$vt_image = array (
					'url' => $resized_img_url,
					'width' => $proportional_size[0],
					'height' => $proportional_size[1]
				);
				
				return $vt_image;
			}
		}

		// no cached files - let's finally resize it
		$new_img_path = image_resize( $file_path, $width, $height, $crop, $jpeg_quality );
		$new_img_size = getimagesize( $new_img_path );
		$new_img = str_replace( basename( $image_src[0] ), basename( $new_img_path ), $image_src[0] );

		// resized output
		$vt_image = array (
			'url' => $new_img,
			'width' => $new_img_size[0],
			'height' => $new_img_size[1]
		);
		
		return $vt_image;
	}

	// default output - without resizing
	$vt_image = array (
		'url' => $image_src[0],
		'width' => $image_src[1],
		'height' => $image_src[2]
	);
	
	return $vt_image;
}

#10 @vteixeira
14 years ago

Just to clarify, the native function is image_resize().

#11 @vteixeira
14 years ago

  • Keywords dev-feedback added

#12 @iandstewart
14 years ago

  • Cc ian@… added

#13 @vteixeira
14 years ago

I have a new version that should work without problems for a subfolder install (the previous version doesn't work with subfolder installs if I pass the image url instead of the ID).

Also I removed the possibility to define the image quality because when I pass this variable WordPress renames the images differently, adding the image quality on the file name instead of the sizes. This caused many problems with the if statements to check if the image exists. So I decided to remove it.

/*
 * Resize images dynamically using wp built in functions
 * Victor Teixeira
 *
 * php 5.2+
 *
 * Exemplo de uso:
 * 
 * <?php 
 * $thumb = get_post_thumbnail_id(); 
 * $image = vt_resize( $thumb, '', 140, 110, true );
 * ?>
 * <img src="<?php echo $image[url]; ?>" width="<?php echo $image[width]; ?>" height="<?php echo $image[height]; ?>" />
 *
 * @param int $attach_id
 * @param string $img_url
 * @param int $width
 * @param int $height
 * @param bool $crop
 * @return array
 */
function vt_resize( $attach_id = null, $img_url = null, $width, $height, $crop = false ) {

	// this is an attachment, so we have the ID
	if ( $attach_id ) {
	
		$image_src = wp_get_attachment_image_src( $attach_id, 'full' );
		$file_path = get_attached_file( $attach_id );
	
	// this is not an attachment, let's use the image url
	} else if ( $img_url ) {
		
		$file_path = parse_url( $img_url );
		$file_path = $_SERVER['DOCUMENT_ROOT'] . $file_path['path'];
		
		//$file_path = ltrim( $file_path['path'], '/' );
		//$file_path = rtrim( ABSPATH, '/' ).$file_path['path'];
		
		$orig_size = getimagesize( $file_path );
		
		$image_src[0] = $img_url;
		$image_src[1] = $orig_size[0];
		$image_src[2] = $orig_size[1];
	}
	
	$file_info = pathinfo( $file_path );
	$extension = '.'. $file_info['extension'];

	// the image path without the extension
	$no_ext_path = $file_info['dirname'].'/'.$file_info['filename'];

	$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension;

	// checking if the file size is larger than the target size
	// if it is smaller or the same size, stop right here and return
	if ( $image_src[1] > $width || $image_src[2] > $height ) {

		// the file is larger, check if the resized version already exists (for $crop = true but will also work for $crop = false if the sizes match)
		if ( file_exists( $cropped_img_path ) ) {

			$cropped_img_url = str_replace( basename( $image_src[0] ), basename( $cropped_img_path ), $image_src[0] );
			
			$vt_image = array (
				'url' => $cropped_img_url,
				'width' => $width,
				'height' => $height
			);
			
			return $vt_image;
		}

		// $crop = false
		if ( $crop == false ) {
		
			// calculate the size proportionaly
			$proportional_size = wp_constrain_dimensions( $image_src[1], $image_src[2], $width, $height );
			$resized_img_path = $no_ext_path.'-'.$proportional_size[0].'x'.$proportional_size[1].$extension;			

			// checking if the file already exists
			if ( file_exists( $resized_img_path ) ) {
			
				$resized_img_url = str_replace( basename( $image_src[0] ), basename( $resized_img_path ), $image_src[0] );

				$vt_image = array (
					'url' => $resized_img_url,
					'width' => $proportional_size[0],
					'height' => $proportional_size[1]
				);
				
				return $vt_image;
			}
		}

		// no cache files - let's finally resize it
		$new_img_path = image_resize( $file_path, $width, $height, $crop );
		$new_img_size = getimagesize( $new_img_path );
		$new_img = str_replace( basename( $image_src[0] ), basename( $new_img_path ), $image_src[0] );

		// resized output
		$vt_image = array (
			'url' => $new_img,
			'width' => $new_img_size[0],
			'height' => $new_img_size[1]
		);
		
		return $vt_image;
	}

	// default output - without resizing
	$vt_image = array (
		'url' => $image_src[0],
		'width' => $image_src[1],
		'height' => $image_src[2]
	);
	
	return $vt_image;
}

#14 @westi
14 years ago

Please attach patches rather than embedding code fragments in comments - https://core.trac.wordpress.org/#HowtoSubmitPatches

#15 @vteixeira
14 years ago

Sorry for that. I was not suposed to embed code in the comment area.

The problem is that I'm not sure it should be a patch right now. I just want to get some feedback from the devs (that's why I added dev-feedback).
And then I made some changes to the code...

#16 follow-up: @Viper007Bond
14 years ago

I'm pretty sure there's already a ticket for this. Mark Jaquith has been wanting this for quite some time.

#17 in reply to: ↑ 16 ; follow-up: @nacin
14 years ago

Replying to Viper007Bond:

I'm pretty sure there's already a ticket for this. Mark Jaquith has been wanting this for quite some time.

Indeed, I had the same thought a few days ago. Couldn't locate one through even looking for tickets by Mark.

#18 @filosofo
14 years ago

I think you're thinking of this ticket and comment.

Really, if you like this idea I think you should take a look at my plugin's implementation, linked to above. It uses the WP API, and I've deployed it on a number of live sites.

#19 in reply to: ↑ 17 @Viper007Bond
14 years ago

There's also a ticket for using a shortcode for images I think so that the URL/size can easily be changed after it's embedded. A good example of this is when you change themes.

#20 follow-up: @vteixeira
14 years ago

I also thought there were already a ticket for this, that's why I searched before posting.

The ticket #8599 doesn't solve our problems since we would still need to register image sizes and all the images would be resized on all that sizes...

I first made this function because I was working on a website where I had more than 12 different 'add_image_size()' on my functions.php file.
Well, with the 3 other sizes that WordPress creates automatically, you can make the math...
For each image uploaded I had 15 different files plus the original - 16 files for one image!

Since I really didn't want to use timthumb I decided to make my own function using what's already available on WP.

But then I thought I could go one step further and make it available to resize any image, not just attachments.
And I also want the theme developer to have complete control over the html - no html generated!

That's it.

I think we need something like this in core for 3.1, everyone needs it.

#21 in reply to: ↑ 20 @nacin
14 years ago

  • Milestone changed from Awaiting Review to Future Release

Replying to vteixeira:

I also thought there were already a ticket for this, that's why I searched before posting.

The ticket #8599 doesn't solve our problems since we would still need to register image sizes and all the images would be resized on all that sizes...

I first made this function because I was working on a website where I had more than 12 different 'add_image_size()' on my functions.php file.
Well, with the 3 other sizes that WordPress creates automatically, you can make the math...
For each image uploaded I had 15 different files plus the original - 16 files for one image!

We still need a whitelist, so we wouldn't deviate from add_image_size(). Otherwise you could just ++ the desired size, and force the server to continue to eat up resources in resizing photos. It's practically a DOS and would be death on a shared host.

But what we could do, is generate those respective sizes on demand, when requested. This also allows for new ones to be generated on theme switch, again on demand.

I think we need something like this in core for 3.1, everyone needs it.

No, this is two months too late for 3.1, and big enough that it would need to be a blessed task to be included in a release.

Also, I found chatter in the IRC logs from March, where Mark was demonstrating what he was working on:
https://irclogs.wordpress.org/chanlog.php?channel=wordpress-dev&day=2010-03-11#m89606. Guess a ticket never came out of it.

#22 @vteixeira
14 years ago

Why the DOS? The images are resized and cached.

They are processed just on the first request and only for that specified size.
I'm not sure why it would eat up resources.

The idea is, like you said, to generate them on demand, and not every size on the upload.

Yes, your suggestion is ok, but we should also count for images that are not attachments (or we doesn't know the ID) like images in custom fields.
So I could register my image sizes and later on I could change those sizes and my images would be regenerated again when they are requested.
The point here is to generate the resized images only when they are requested and not on upload so I don't end up with lots of image files lying around.

#23 follow-up: @Viper007Bond
14 years ago

I think nacin is assuming that the width/height parameters can come from the URL rather than being hard coded into your PHP. If that was the case, you could just play with the GET values to make your code generate tons and tons of random sized images.

Anyway, the media API already supports both named and array width/height based parameters, so there's no good reason to deviate from the existing code and reinvent the wheel.

Infact your code would very, very easily be implemented as a plugin without a single theme change to call your function. On WordPress.com for example, we generate no thumbnails on upload and instead generate them on the fly as needed (although we use query strings to do so) and that's all done using hooks. You could do the same.

I'd still like to get this into core though, but we need to make sure to avoid issues like the resize failing and then each request attempting to resize it again.

#24 @pross
14 years ago

  • Cc pross@… added

#25 @cais
14 years ago

  • Cc cais added

#26 @newkind
14 years ago

  • Cc newkind added

#27 @navjotjsingh
13 years ago

  • Cc navjotjsingh@… added

#28 @goto10
13 years ago

  • Cc goto10 added

#29 @wlindley
13 years ago

I faced this problem some years ago and wrote the AutoNav plugin -- http://wordpress.org/extend/plugins/autonav/ -- in response.

However I took a different approach, parts of which might be valuable in core. My plugin lets you specify a three sizes for thumbnails, depending on whether you want attachments or child pages listed in 2, 3, or 4 columns (those numbers maybe changed in the admin) but when the page is actually displayed, the plugin looks through the directory to find candidate images that, say, fit in 200 pixels wide or 150 pixels tall.

This plugin -- http://wordpress.org/extend/plugins/regenerate-thumbnails/ -- recreates all thumbnails, but wastes much time remaking existing downsized images without checking whether it needs to (presumably, one could check existing file times and skip downsized images with timestamps after their parent). The Wordpress database contains a list of image sizes for each attachment, created by wp's internal functions when the downsize functions create images for registered sizes; but this plugin blitzes that list and starts over.

Any resize operation should ideally add information about what it did, to the list of image sizes associated with the attachment. This should also include whether the downsize image was "fit" or "cropped" -- which isn't there now.

The biggest challenge I see with any of these schemes is the disconnect between what's in the database and what's on disk. A user could well hand-modify a particular attachment size to retouch the sharpness; how would we know not to overwrite it? An automatically-created downsized image for an "old" size might not be used anywhere on the site -- although we would probably have to somehow scan the text of every post to be sure! -- but can we delete an image without breaking possibly links on other sites?

In short, how can we delete obsolete images to free space, without any way of knowing which imagess really are unused?

#30 @Viper007Bond
13 years ago

This plugin -- http://wordpress.org/extend/plugins/regenerate-thumbnails/ -- recreates all thumbnails, but wastes much time remaking existing downsized images without checking whether it needs to (presumably, one could check existing file times and skip downsized images with timestamps after their parent). The Wordpress database contains a list of image sizes for each attachment, created by wp's internal functions when the downsize functions create images for registered sizes; but this plugin blitzes that list and starts over.

If my plugin were to do that, it would require maintaining a modified version of wp_generate_attachment_metadata() (the single function my currently uses to generate the thumbnails).

#31 @paulomagalhaes
13 years ago

  • Cc paulomagalhaes added

This function has been forgotten? WordPress 3.2 requirement: PHP 5.2.4... also would be nice to edit the image crop within the "Edit Media Library".

#32 @johnnytee
13 years ago

  • Cc johnnytee added

#33 @aharner
12 years ago

I had run into an issue where I used direct URLs to my images that I was generating from a custom database, and in those URLs I had converted spaces to '%20' for proper URLs to my images.

because the file structure had them as spaces, I had to do a quick str_replace to fix the problem.

figured I'd just make this note here incase anyone else ever runs into that issue:


//after this line:
$actual_file_path = ltrim( $file_path['path'], '/' );
$actual_file_path = rtrim( ABSPATH, '/' ).$file_path['path'];

//ADDED THIS to replace '%20' from the URL with spaces:
$actual_file_path = str_replace('%20', ' ', $actual_file_path);

#34 @pauldewouters
12 years ago

  • Cc pauldewouters added

#35 @dboulet
12 years ago

  • Cc dboulet added

#36 @husobj
12 years ago

  • Cc ben@… added

#37 @technosailor
12 years ago

  • Cc aaron@… added

#38 @technosailor
12 years ago

Love to see this targetted for 3.5. This really worked well for a project I just worked on.

#39 @wpsmith
12 years ago

  • Cc travis@… added

#40 @sc0ttkclark
12 years ago

  • Cc lol@… added

Used this too, works great, would love to see something in core for these types of things.. +1 for 3.5

#41 @jeremyfelt
12 years ago

  • Cc jeremy.felt@… added

#42 @bainternet
12 years ago

  • Cc admin@… added

#43 in reply to: ↑ 23 ; follow-ups: @scribu
12 years ago

Replying to Viper007Bond:

I'd still like to get this into core though, but we need to make sure to avoid issues like the resize failing and then each request attempting to resize it again.

Here's my take on the whole thing:

  1. Add an 'immediate' parameter to each intermediate size. 'immediate' => true means the file is generated right after the attachment is uploaded.
  1. For 'immediate' => false sizes, generate it on the fly, only if it's not present in the array returned by wp_get_attachment_metadata().

#44 in reply to: ↑ 43 @sc0ttkclark
12 years ago

Replying to scribu:

Here's my take on the whole thing:

  1. Add an 'immediate' parameter to each intermediate size. 'immediate' => true means the file is generated right after the attachment is uploaded.
  1. For 'immediate' => false sizes, generate it on the fly, only if it's not present in the array returned by wp_get_attachment_metadata().

This is totally acceptable for me and the use cases I've seen. Would this be added to add_image_size?

#45 @scribu
12 years ago

Yep, and I think the built-in image sizes should also go through add_image_size().

#46 @jb510
12 years ago

  • Cc jbrown510@… added

#47 @mercime
12 years ago

  • Cc mercijavier@… added

#48 @daltonrooney
12 years ago

  • Cc dalton@… added

#49 @daltonrooney
12 years ago

Thanks so much to Victor, this feature is something I've really been looking for in WordPress. I did find a small bug and hopefully a satisfactory solution. The cropped and non-cropped images are both created with the same dimensions, so if the cropped image already exists and you try to specify the non-cropped image instead, the cropped image is still returned. My solution is to calculate the final dimensions of the image earlier on.

Just before this:

$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension; 

Add this:

/* Calculate the eventual height and width for accurate file name */

if ( $crop == false ) {
   $proportional_size = wp_constrain_dimensions( $image_src[1], $image_src[2], $width, $height );
   $width = $proportional_size[0];
   $height = $proportional_size[1];
}
	
$cropped_img_path = $no_ext_path.'-'.$width.'x'.$height.$extension;

#50 @daltonrooney
12 years ago

No way to revise/delete attachments, huh? I accidentally left an echo statement for troubleshooting in the previously attached file. Please delete line 64 if you download my updated version of the function.

#51 @scribu
12 years ago

You should be able to re-upload an attachment with the same name.

@daltonrooney
12 years ago

Small update to predict final image size before naming it.

#52 @emzo
12 years ago

  • Cc emzo added

#53 in reply to: ↑ description @dimas18
12 years ago

Hello buddies

i found this script in one of my current theme and it look very nice lightweight and presice,i wondering if this script can work with a sub domain example, i upload a image with my principal domain but, when it is request by visitor send a url with my sub domain linked to a folder where the image whas uploaded like img.subdomain.com

thanks

#54 @techguytom
12 years ago

  • Cc techguytom added

#55 @bmonster99
12 years ago

  • Cc bmonster99 added

#56 @webord
12 years ago

  • Cc webord.net@… added

#57 @willmot
12 years ago

  • Cc willmot added

#58 @anointed
12 years ago

+1 for considering adding to 3.5. I still use this function in all of my themes and it would be nice to have it checked for security and added to core for peace of mind.

#59 @mdgl
12 years ago

If this feature is being considered for 3.5, may I suggest we also consider proper support for uploaded SVG images at the same time? I think this may even help us achieve the most appropriate design for the proposed "dynamic image resizing" feature.

Presently, you can easily upload SVG files (by modifying the allowable MIME types through a plug-in) but they are not treated as "images", merely as opaque attachments. You can (perhaps) also embed SVG files in (X)HTML where they are effectively invisible to WP and not reusable.

I believe you should be able to upload SVG image files (with a default size), have them displayed and treated as images by WP and subsequently reuse them in different places and at different sizes through the HTML <img> tag.

The key to this I think is separating the "size" attributes from the stored image file and its use(s) in posts/pages. That is, when you upload an image file it is stored in the library with a default size (actual image size in the case of bitmaps, declared size in the case of SVG). When you make use of an image (e.g. by attaching it to a post) you should be able to declare the actual size to be used for the image in that particular instance. In these cases, WP can choose and/or dynamically generate an appropriate bitmap or simply let the browser scale the SVG automatically to the dimensions specified on the <img> tag.

@foxinni
12 years ago

Resize script to support MultiSite

#60 @nemor
12 years ago

foxinni, have you tried your multisite script on a local wordpress installation? I'm trying to use it, but it gives me this error:
Warning: getimagesize(C:/xampp/htdocs/wp-content/blogs.dir/3/files/2012/02/myimage.jpg) function.getimagesize: failed to open stream: No such file or directory in C:\xampp\htdocs\wordpress\wp-content\themes\supermassive\lib\scripts\image-resizer.php on line 52

The image is shown anyway, but in it's original size, not resized.

Last edited 12 years ago by nemor (previous) (diff)

#61 @markoheijnen
12 years ago

nemor: that is a problem on your local environment.

There is also a change that this ticket will be invalidated/solved because of the work on #6821 to simplify some of the functions for better use cases like this.

Last edited 12 years ago by markoheijnen (previous) (diff)

#62 @MatthewRuddy
12 years ago

Anyway we can get this to work through a URL or an alternative? Currently using Ajax for getting resized versions of images to be replace via Javascript. Gets a bit much when there are a lot of images.

#63 @scribu
12 years ago

Related: #19393

#64 @alexvorn2
12 years ago

  • Cc alexvornoffice@… added

#65 @scribu
12 years ago

Related: #21961

#66 @ocean90
12 years ago

#21961 was marked as a duplicate.

#67 follow-up: @ocean90
12 years ago

Interesting comment by scribu: ticket:21961#comment:3.

#68 in reply to: ↑ 67 @Viper007Bond
12 years ago

Replying to ocean90:

Interesting comment by scribu: ticket:21961#comment:3.

Good ol' race conditions.

At the hack day after WordCamp San Francisco, a bunch of us sat down and chatted about this for quite a while. The solution we came up we called "just too late" generation (as opposed to "just in time" aka on-demand). Missing thumbnails get served as browser-resized fullsize images and a thumbnail generation gets queued via WP Cron IIRC.

This issue is certainly highly complicated though and will need significant discussion before any code is actually written. The real work will be done via the planning rather than the code itself.

#69 @viniciusmassuchetto
12 years ago

Another reference: I wrote a plugin using 'phpThumb' and ImageMagick's 'convert' to generate and cache custom image sizes. It also supports shortcodes.

#70 @jaredatch
12 years ago

  • Cc jared@… added

@prettyboymp
12 years ago

Patch piggy backs off of 22100.2.diff in ticket @22100

#71 @prettyboymp
12 years ago

  • Cc mpretty@… added
  • Keywords has-patch added

Patch is based off of 22100.2.diff from #22100.

I do have concerns about the way I had to implement the callback since the image generation needs to happen under the wp-admin, so normal cron won't work. However, the post back will fail for sites that have the admin blocked with .htaccess. May want to consider setting a default static definition that turns off the ability to have late generated image sizes for this release and then creating a more robust way to run 'admin only' crons in a future release.

#72 @MatthewRuddy
12 years ago

Hey all, just thought I'd throw my own solution into the mix. I've developed a function that replicates Timthumb resizing exactly for seamless integration. I found the OP vt_resize function wouldn't crop the image in the same way to the exact dimensions specified.

I've been developing a slideshow plugin, so I needed images to be resized and cropped to exactly the dimensions I specified (just like Timthumb). I actually took the crop positioning calculations straight from Timthumb itself. You can have a look at the Github page below I've created for it.

http://matthewruddy.github.com/Wordpress-Timthumb-alternative

It has an alternative functions for Wordpress 3.5, which has added handy new image manipulation API. That said, I did find the new API to be slightly slower than the function provided for older Wordpress versions, but that's probably because there is a lot more going on (checks for GD or Imagemagik, etc).

#73 @willmot
12 years ago

Here's one of our plugins that implements on the fly image resizing.

https://github.com/humanmade/WPThumb

Under the hood it uses phpthumb which obviously wouldn't be appropriate for core but it would be easy to replace that with the new image resizing stuff in 3.5 (in-fact I think @markoheijnen is working on it, as are we).

It's well tested (40 odd unit tests) and runs on some pretty large sites (10's of millions of uniques per month).

#74 @alexvorn2
12 years ago

the problem is that your plugins does not provide the image aligment: top/bottom as timthumb does. one static "image.jpg" should be dynamically resized to this name image-$width-$height-$aligment-$quality-$crop.jpg so after the image was resized, another image could be made if the settings of quality, cropped or not, alignment have been changed...

#75 follow-up: @willmot
12 years ago

I'm not sure what you mean by image alignment? Are you referring to the crop position? If so WP Thumb fully supports that (top, left, center, etc), it doesn't include the args in the resulting image name however as that can lead to long filenames and also could possibly be a security risk as you are exposing input data. You can generate as many different images from the same source as you want, each version is created and cached.

#76 in reply to: ↑ 75 @alexvorn2
12 years ago

yes crop position, after the images was cropped and created, and if the user wants other crop position that he must first delete the old image before generating a new one.
I don't see a problem in long file and security risks? crop position or image quality can be a security risk?

#77 follow-up: @willmot
12 years ago

This probably isn't the best place to be arguing about our respective plugins :-), however, WP Thumb doesn't require you to delete the old image before generating a new one with a different crop position, have you used it? You can have as many different images as you want, each with different args (crop position, quality, etc.)

#78 in reply to: ↑ 77 @alexvorn2
12 years ago

Replying to willmot:

This probably isn't the best place to be arguing about our respective plugins :-), however, WP Thumb doesn't require you to delete the old image before generating a new one with a different crop position, have you used it? You can have as many different images as you want, each with different args (crop position, quality, etc.)

You are right. I have not used it.

#79 @aaronholbrook
11 years ago

  • Cc aaron@… added

#80 @esauvisky
11 years ago

Guys, I have a RewriteRule on .htaccess from / to /wordpress (RewriteRule (.*) /wordpress/$1 [L]) for stripping /wordpress from my urls, and I was getting the error No such file or directory because $filepath was wordpress/wp-content/uploads[...].

I fixed it with an disgusting preg_replace('/^.wordpress\//'), but I would like to know if there's a better way of doing it. Thanks!

Version 0, edited 11 years ago by esauvisky (next)

#81 @travisnorthcutt
11 years ago

  • Cc travis@… added

#82 @kraftbj
11 years ago

  • Cc bk@… added

#83 @bradyvercher
11 years ago

  • Cc brady@… added

#84 @francescolaffi
11 years ago

  • Cc francesco.laffi@… added

#85 in reply to: ↑ 43 @husobj
11 years ago

  • Cc ben@… removed

Replying to scribu:

Here's my take on the whole thing:

  1. Add an 'immediate' parameter to each intermediate size. 'immediate' => true means the file is generated right after the attachment is uploaded.
  1. For 'immediate' => false sizes, generate it on the fly, only if it's not present in the array returned by wp_get_attachment_metadata().

I think adding this to the add_image_size() function would be an elegant way to implement new image sizes for generation on-demand (or just after)

#86 @tar.gz
11 years ago

  • Cc code@… added

#87 @alex-ye
11 years ago

  • Cc nashwan.doaqan@… added

#88 in reply to: ↑ 43 @NickDC
11 years ago

Replying to scribu:

  1. Add an 'immediate' parameter to each intermediate size. 'immediate' => true means the file is generated right after the attachment is uploaded.
  1. For 'immediate' => false sizes, generate it on the fly, only if it's not present in the array returned by wp_get_attachment_metadata().

This is a great approach. Is anyone working on a patch for this?

#89 @SergeyBiryukov
11 years ago

#25884 was marked as a duplicate.

#90 @SergeyBiryukov
11 years ago

#21295 was marked as a duplicate.

#91 @drrobotnik
10 years ago

Is anyone active on this issue? Since a lot of blogs are running on shared hosting media bloat can cause issues with maintenance. I've inherited a few photography projects where the hosting company has given a deadline to trim their media folder. Would everyone be in agreement that an on the fly solution would be the best place to start?

Media bloat Ref:#25884

#92 @SergeyBiryukov
10 years ago

#27395 was marked as a duplicate.

#93 @helen
10 years ago

#28438 was marked as a duplicate.

This ticket was mentioned in IRC in #wordpress-dev by SergeyBiryukov_. View the logs.


10 years ago

This ticket was mentioned in Slack in #core by eric. View the logs.


9 years ago

This ticket was mentioned in Slack in #feature-respimg by drrobotnik. View the logs.


9 years ago

@ericlewis
9 years ago

#97 @nacin
9 years ago

#8599 was marked as a duplicate.

#98 @ericlewis
9 years ago

It's been a while. I'd like to reorient our intent on this ticket - alleviating issues with missing image files for custom sizes.

Accepting requests for generating any size image on the fly (e.g. 30x300, 87x239) is dangerous, and opens up the server for latency of cropping an image. So, this doesn't sound like a good solution for WordPress core, which must work on the lowest common denominator of hosting environments.

A thought that nacin shared recently was creating an image file for an image size on the fly if it fits the whitelist of registered image sizes. This balances the expectations of the user (where's my image?) with the internal APIs of WordPress. If a theme registers an image size, we can guarantee a file will exist for that image size. I think that fits the bill for this ticket quite well.

Consider attachment:15311.2.diff a proof of concept for this approach.

Last edited 9 years ago by ericlewis (previous) (diff)

#99 @jb510
9 years ago

+1 to whitelisted image sizes. Without that it'd be trivial to DOS a site a site just by requesting random image sizes.

#100 @drrobotnik
9 years ago

#30874 is my approach based on that conversation. I modify the request on image_downsize. The patch isn't complete but I think this approach is pretty slim as it enhances the functions in place and doesn't change how the end theme/plugin devs would use it.

https://core.trac.wordpress.org/attachment/ticket/30874/media.patch

#101 @markoheijnen
9 years ago

https://core.trac.wordpress.org/attachment/ticket/21295/21295.diff is my approach. Which is the same as drrobotnik except the location is different.

@ericlewis
9 years ago

#102 @ericlewis
9 years ago

markoheijnen and drrobotnik's patches are great, and apply the problem at the underlying API level. This is more helpful than what I did in attachment:15311.2.diff, to generate the image for a whitelisted image URL request. I wonder if the latter has utility - my guess is no.

I've collated both of the two patches, adding documentation and preferring internal functions where we can.

I've also juggled the logic a bit, giving image_get_intermediate_size() a chance, then consider generating an image on the fly, then falling back to original.

@ericlewis
9 years ago

@ericlewis
9 years ago

#103 @ericlewis
9 years ago

In attachment:15311.5.diff,

  • move this logic in image_get_intermediate_size(), as markoheijnen had in his patch. This is the canonical function for attempting to get a file for an image size, which other functions like image_downsize() proxy to.
  • add a unit test.
  • give preference of cutting from the large size if it exists and is big enough, otherwise fallback to the original.
Last edited 9 years ago by ericlewis (previous) (diff)

#104 @markoheijnen
9 years ago

@Eric: is 15311.5.diff the right patch? I don't see a unit tests.

I believe it has some utilities if people do want to enable resizing on the fly. That said, your patch should include a way to be able to load the requested image since WordPress will skip an image size when it doesn't exist and search for the closest best. The benefit of 15311.5.diff is that the page request isn't slowed down. Unsure about possible breakage when rewrite rules aren't correct (as in ignoring when extension has been passed).

Your first reasoning is right but the code doesn't reflect that. You now put it in image_downsize() like drrobotnick did. I put it in image_get_intermediate_size() since that function gets requested by image_downsize(). So if plugin developers would call that instead everything still is fine.

Personally I like my approach better where I only generating the missing image and not regenerate everything which is what wp_generate_attachment_metadata() does.

@ericlewis
9 years ago

#105 @ericlewis
9 years ago

@markoheijnen - sorry, you're right. attachment:15311.6.diff is the right version moving the code to image_get_intermediate_size() and includes unit tests.

@ericlewis
9 years ago

#106 follow-up: @ericlewis
9 years ago

Replying to markoheijnen:

Personally I like my approach better where I only generating the missing image and not regenerate everything which is what wp_generate_attachment_metadata() does.

Good point. wp_generate_attachment_metadata() regenerates all image sizes - yikes. In attachment:15311.7.diff, let's use the return from image_make_intermediate_size() to fill in the missing info on $imagedata more explicitly, and then hit wp_update_attachment_metadata().

Should/can we do anything about scribu's race condition? Essentially, should we worry about the same size being requested for generation 100x in a second to avoid server exhaustion.

I could see setting a lock in post meta on the attachment. Say, image_size_{size}_generating, which is checked before generating, then set, then unset when the image is done generating. This could avoid a resource stampede, but creates more db queries.

#107 in reply to: ↑ 106 @drrobotnik
9 years ago

Replying to ericlewis:

Should/can we do anything about scribu's race condition? Essentially, should we worry about the same size being requested for generation 100x in a second to avoid server exhaustion.

If we take this on would moving wp_update_attachment_metadata to actually run before the image size is generated? It's possible via $editor->get_output_format or $editor->generate_filename. I assume image_size_{size}_generating would have the same outcome to the racers when the page loads. The lucky winner won't see the image?

This ticket was mentioned in Slack in #core by nacin. View the logs.


9 years ago

#109 @bfintal
9 years ago

Here's my approach in plugin form: https://wordpress.org/plugins/otf-regenerate-thumbnails/

It doesn't resize when there's a size already generated. It's not at all at it's perfect form.

#110 @alexvorn2
9 years ago

I have a similar plugin that resize the images on the fly too - https://wordpress.org/plugins/resize-images-in-posts/
please give a try and say what you think about it ;)

Last edited 9 years ago by alexvorn2 (previous) (diff)

#111 @Teeks
9 years ago

Instead of patching image_get_intermediate_size I used the filter in image_downsize. Gist link below.

I think it's better to approach this with filters in a theme, rather than changing core methods, because on-the-fly image sizing is more often a theme specific requirement.

There's a hefty long description in the gist doc block, please let me know if this helps or you improve on it:

https://gist.github.com/timkinnane/e82eb87d9cc489620b80

Last edited 9 years ago by Teeks (previous) (diff)

This ticket was mentioned in Slack in #core by eric. View the logs.


9 years ago

This ticket was mentioned in Slack in #feature-respimg by drrobotnik. View the logs.


9 years ago

This ticket was mentioned in Slack in #feature-respimg by helen. View the logs.


9 years ago

#115 @benoitchantre
9 years ago

It would be nice to have that in Wordpress 4.3

This ticket was mentioned in Slack in #feature-imageflow by joemcgill. View the logs.


9 years ago

#117 @FolioVision
9 years ago

Hi Guys,

What about simply generating the missing image sizes on save (assuming those sizes are going into content)? There's a nice hook for on save. This would work for the original poster's issue.

If an entire theme's thumbnail image sizes are changed, perhaps there should be a large button to regenerate images throughout the website. When a user has to push a button to get an action, consequences are on the users shoulders (especially if we warn them with a javascript warning and confirm button). For regeneration, we should probably pace it by default so it does not use all of a server's resources.

Alec Kinnear

#118 @sandys11
9 years ago

guys, just a forward thinking question - how will this integrate with Amazon S3?

For example, if we are building plugins that will upload an image directly to Amazon S3 (and not keep any local images). Can this feature and hooks be built in a manner that removes the need for an on-disk "gallery" or "uploads" folder?

This question extends to other blob managers as well - Azure, Rackspace, etc. This seriously increases the reliability and horizontal scalability of any Wordpress installation.

This ticket was mentioned in Slack in #core by mike. View the logs.


9 years ago

#120 @j-falk
8 years ago

Isn't #6814 related to this issue?

From it's description:
The upload part of the new multi-uploader is pretty nice now, but it blocks on the "crunching" phase, which can sometimes take 20-60 seconds, I assume to create medium thumbnails and such.

The crunching part of the upload should not block the next file beginning the upload process, it should happen asynchronously with the rest of the process.

This ticket was mentioned in Slack in #core by j-falk. View the logs.


8 years ago

This ticket was mentioned in Slack in #core by pento. View the logs.


8 years ago

This ticket was mentioned in Slack in #core by swissspidy. View the logs.


8 years ago

This ticket was mentioned in Slack in #core-images by sebastian.pisula. View the logs.


8 years ago

This ticket was mentioned in Slack in #core-images by mike. View the logs.


8 years ago

This ticket was mentioned in Slack in #core-media by mike. View the logs.


7 years ago

This ticket was mentioned in Slack in #core-media by mike. View the logs.


6 years ago

#129 @SergeyBiryukov
5 years ago

#47583 was marked as a duplicate.

#130 @azaozz
5 years ago

#21295 was marked as a duplicate.

#131 @joemcgill
4 years ago

#49552 was marked as a duplicate.

#132 @joemcgill
4 years ago

#34949 was marked as a duplicate.

Note: See TracTickets for help on using tickets.