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

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