How do I delete all generated images from my server except those currently used in posts

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.

1 Answer
1

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.

Leave a Comment