4

I have a new set of image sizes for my site's in-development theme. My server is filled with old image sizes, most of which have never been used. You've heard this one before, right?

The Question
How do I delete all WP generated thumbnails from my server but not those thumbnails currently used in posts?

That is to say something like: $ find . -name "*-*x*.jpg" | xargs rm -f is too wide of a hammer because myimage-300x250.jpg might be used in a post. Its 6 other variations are welcome to go away though!

I think I need to generate a list of used images, compare that to my directory and delete the difference (minus the originals). But how to go about that escapes me.


The "why?" sidebar:
Extra image sizes on the server is generally no big deal but we have over 2.7 gigs in their right now and it just seems a waste to leave them and back them up. Plus, we're about to start fresh - why not do the spring cleaning?

As far as regenerating the thumbs, while the new design has new image specifications, none of us have the time to go and update hundreds of older posts with RTE inserted images. Let bygones be bygones, I say.

3
  • How about deleting unattached images from your Media library Feb 26, 2014 at 4:20
  • Thanks @BradDalton but that wont touch the generated thumbnails.
    – Will
    Feb 26, 2014 at 5:18
  • You could delete the unattached files from the media library and then use Force regenerate thumbnails to delete the thumbnails that are the wrong sizes.
    – mantis
    Feb 26, 2014 at 11:27

1 Answer 1

6

This should get you started:

  1. Loop over all posts and build an array of linked image files
  2. Loop over all image attachments & cross check if any of them have been linked to

Might need to batch it or boost the timeout & memory limit, depending on the server & number of posts you're working with.

$posts = get_posts(
    array(
        'posts_per_page' => -1,
        'post_type' => array( 'post', 'page' ),
    )
);

$files = array();
foreach ( $posts as $post ) {
    if ( preg_match_all( '/src=(?:\'|")(.+?)(?:\'|")/', $post->post_content, $matches ) ) {
        foreach ( $matches[1] as $url ) {
            // Replace "wp-content/uploads/" with your upload directory if not default setup
            if ( preg_match( '!wp-content/uploads/(.+)$!', $url, $matches_2 ) )
                $files[] = $matches_2[1];
        }
    }
}

$posts = get_posts(
    array(
        'posts_per_page' => -1,
        'post_mime_type' => 'image',
        'post_type' => 'attachment',
        'fields' => 'ids',
    )
);

update_postmeta_cache( $posts );

foreach ( $posts as $post_id ) {
    if ( is_file( $file = get_attached_file( $post_id ) ) ) {
        if ( $meta = wp_get_attachment_metadata( $post_id ) ) {
            $path = dirname( $file );

            if ( preg_match( '!^[0-9]{4}/[0-9]{2}/!', $meta['file'], $matches ) ) {
                // Split date folders with filename for when searching size variations
                $suffix = $matches[0];
            } else {
                $suffix = '';
            }

            if ( ! in_array( $meta['file'], $files ) ) {
                // Original file not in post content
                // unlink( $file ); 
            }

            foreach ( $meta['sizes'] as $name => $data ) {
                if ( ! in_array( $suffix . $data['file'], $files ) ) {
                    // Image size not in post content
                    // unlink( "$path/{$data['file']}" );
                }
            }
        }       
    } else {
        wp_delete_post( $post_id, true ); // Might as well clean up
    }
}

What you do with this is up to you. If you're going to keep the original but delete size variations, make sure you wipe it's existence from the database entry in $meta['sizes'] and save with wp_update_attachment_metadata( $post_id, $meta ).

You could even go so far as to regenerate the new sizes with wp_get_image_editor() & update the old src's in your post content on-the-fly.

3
  • Thanks again. Striking how much I just learned about WordPress (and some php!) playing around with your answer! One edit: I had to remove the last $ from '!wp-content/uploads/(.+)!$'.
    – Will
    Feb 26, 2014 at 20:25
  • PS. I think you'd get a warm welcome from folks if you wrapped this into a plugin.
    – Will
    Feb 26, 2014 at 20:25
  • @Will One day :) And the $ in the regex should've been before the !(answer updated). Feb 27, 2014 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.