Downsizing Many Large Images Attached to Posts, in Bulk?

(Moderator’s note: The original title was: “Shrink full size images in post”)

I built a WordPress site for someone and they entered a ton of posts with images that are wider than the content area. Is there a way to shrink all of the image attachements to use a max width? They are entered as “full size”, not as thumbnail, medium, etc.

4 Answers
4

Great question! WordPress lacks some of the higher level imaging management features that would make it file maintenance like you need so much easier. The core WordPress team has been threatening to add enhance image features for many versions; eventually they might actually do it! Until then you’ll have to use a patchwork of plugins and/or custom code.

More good news and bad news: The good news is WordPress combined with PHP has tons of lower level imaging handling features but the bad news is it has tons of lower level imaging handling features from which you have to decide which to use! A while back I posted a list of image handling functions found in WordPress and if you’ll scan it you’ll probably see there are several different ways to skin this cat.

That said, I picked one approach and it follows below with copious comments rather than explain here in the text. You can just copy it to the root of your website as something like /downsize-images.php and call it from your browser. If it times out, don’t worry; I wrote it to keep track of what it had already done so just keep running it until you see it print “Done!”

NOTE: You need to set the constant MAX_RESIZE_IMAGE_TO to whatever you want your maximum dimension to be; my example used 640 pixels. After it runs you’ll have a bunch of image files with an extension of .save appended. Once you are happy that it ran like you wanted you can just delete those .save files. I didn’t automatically delete them in case something went wrong with the script.

(IMPORTANT: Be SURE TO BACKUP both your database and your upload directory BEFORE you run this. It worked on my machine but you might have something I didn’t test and it might cause corruption so backup! DO NOT come back later and say I didn’t warn you!!!):

<?php

include "wp-load.php";                 // Need this to load WordPress core
include "wp-admin/includes/image.php"; // Needed for wp_create_thumbnail()

define('MAX_RESIZE_IMAGE_TO',640);     // SET YOUR MAX DIMENSION HERE!!!!

// Check our "queue" to see if we've previously only run to partial competion.
$attachment_ids = get_option('attachment_ids_to_resize');
if ($attachment_ids=='Done!') { // Make sure we don't do it again
  echo 'Already done.';
} else {
  if (empty($attachment_ids)) { // If this is the first time, this array will be empty. Get the IDs.
    global $wpdb;
    $attachment_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type="attachment"");
  }
  foreach($attachment_ids as $index => $attachment_id) {
    // Get metadata: [width,height,hwstring_small,file,sizes[file,width,height],image_meta[...]]:
    $metadata = wp_get_attachment_metadata($attachment_id);
    // Get upload_dir: [path,url,subdir,basedir,baseurl,error]:
    $upload_dir = wp_upload_dir();
    // Get full path to original file
    $filepath = "{$upload_dir['basedir']}/{$metadata['file']}";
    // Make a smaller version constrained by largest dimension to MAX_RESIZE_IMAGE_TO
    $smaller_file = wp_create_thumbnail( $filepath, MAX_RESIZE_IMAGE_TO);
    // If the file was really created
    if (@file_exists($smaller_file)) {
      // Append a ".save" to original file name, just in case (you can manually delete *.save)
      rename($filepath,"{$filepath}.save");
      // Get this smaller files dimensions
      list($width,$height) = getimagesize($smaller_file);
      // Strip the base directory off the filename (i.e. '/Users/username/Sites/sitename/wp-content/uploads/')
      $metadata['file'] = str_replace("{$upload_dir['basedir']}/",'',$smaller_file);
      $metadata['height'] = $height;
      $metadata['width'] =  $width;
      // Get the the small size, 128x96 or smaller if need be
      list($uwidth,$uheight) = wp_constrain_dimensions($metadata['width'], $metadata['height'], 128, 96);
      $metadata['hwstring_small'] = "height="$uheight" width="$uwidth"";
      // Make sure none of the sizes are larger than the new size
      foreach($metadata['sizes'] as $size => $size_info) {
        if (intval($size_info['height'])>intval($height) ||
            intval($size_info['width'])>intval($width)) {
          // If too large get the full file path of the sized image
          $old_file = "{$upload_dir['path']}/{$size_info['file']}";
          // Tack a ".save" extension on it, just in case (you can manually delete *.save)
          rename($old_file,"{$old_file}.save");
          // Remove it from the metadata since it's too large
          unset($metadata['sizes'][$size]);
        }
      }
      // Update the attachement metadata with the new smaller values
      wp_update_attachment_metadata($attachment_id,$metadata);
      // Update the name of the other place the orginal file name is stored
      update_attached_file($attachment_id,$metadata['file']);
    }
    // Remove this attachment from our "queue"
    unset($attachment_ids[$index]);
    // Save the new smaller "queue" in case we need to run this again.
    update_option('attachment_ids_to_resize',$attachment_ids);
  }
  // If we've actually processed them all
  if (count($attachment_ids)==0) {
    // Store a marker so we won't accidentally run this again
    update_option('attachment_ids_to_resize','Done!');
  }
  echo 'Done!';
}

Leave a Comment