Make WordPress Core

Changeset 46076


Ignore:
Timestamp:
09/07/2019 01:06:49 AM (5 years ago)
Author:
azaozz
Message:

Media: Add handling for "BIG" images. When the users upload a big image, typically a photo, scale it down to make it suitable for web use. Then use the scaled image as the "full" size, and keep the originally uploaded image for creating high quality sub-sizes in the future and in case the users want to download it later.

Introduces wp_get_original_image_path() that retrieves the path to the originally uploaded image in all cases, and big_image_size_threshold filter to set the pixel value above which images will be scaled. The same value is used as max-width and max-height when scaling.

See #47873.

Location:
trunk/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/image.php

    r45934 r46076  
    137137function wp_update_image_subsizes( $attachment_id ) {
    138138    $image_meta = wp_get_attachment_metadata( $attachment_id );
    139     $image_file = get_attached_file( $attachment_id );
     139    $image_file = wp_get_original_image_path( $attachment_id );
    140140
    141141    if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
     
    143143        // If there is an uploaded file, make all sub-sizes and generate all of the attachment meta.
    144144        if ( ! empty( $image_file ) ) {
    145             return wp_create_image_subsizes( $image_file, array(), $attachment_id );
     145            return wp_create_image_subsizes( $image_file, $attachment_id );
    146146        } else {
    147147            return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) );
     
    168168 *
    169169 * @param string $file          Full path to the image file.
    170  * @param array  $image_meta    The attachment meta data array.
    171170 * @param int    $attachment_id Attachment Id to process.
    172  * @return array The attachment metadata with updated `sizes` array. Includes an array of errors encountered while resizing.
    173  */
    174 function wp_create_image_subsizes( $file, $image_meta, $attachment_id ) {
    175     if ( empty( $image_meta ) || ! isset( $image_meta['width'], $image_meta['height'] ) ) {
    176         // New uploaded image.
    177         $imagesize            = @getimagesize( $file );
    178         $image_meta['width']  = $imagesize[0];
    179         $image_meta['height'] = $imagesize[1];
    180 
    181         // Make the file path relative to the upload dir.
    182         $image_meta['file'] = _wp_relative_upload_path( $file );
    183 
    184         // Fetch additional metadata from EXIF/IPTC.
    185         $exif_meta = wp_read_image_metadata( $file );
    186 
    187         if ( $exif_meta ) {
    188             $image_meta['image_meta'] = $exif_meta;
     171 * @return array The image attachment meta data.
     172 */
     173function wp_create_image_subsizes( $file, $attachment_id ) {
     174    $imagesize = @getimagesize( $file );
     175
     176    if ( empty( $imagesize ) ) {
     177        // File is not an image.
     178        return array();
     179    }
     180
     181    // Default image meta
     182    $image_meta = array(
     183        'width'  => $imagesize[0],
     184        'height' => $imagesize[1],
     185        'file'   => _wp_relative_upload_path( $file ),
     186        'sizes'  => array(),
     187    );
     188
     189    // Fetch additional metadata from EXIF/IPTC.
     190    $exif_meta = wp_read_image_metadata( $file );
     191
     192    if ( $exif_meta ) {
     193        $image_meta['image_meta'] = $exif_meta;
     194    }
     195
     196    /**
     197     * Filters the "BIG image" threshold value.
     198     *
     199     * If the original image width or height is above the threshold, it will be scaled down. The threshold is
     200     * used as max width and max height. The scaled down image will be used as the largest available size, including
     201     * the `_wp_attached_file` post meta value.
     202     *
     203     * Returning `false` from the filter callback will disable the scaling.
     204     *
     205     * @since 5.3.0
     206     *
     207     * @param array  $imagesize     Indexed array of the image width and height (in that order).
     208     * @param string $file          Full path to the uploaded image file.
     209     * @param int    $attachment_id Attachment post ID.
     210     */
     211    $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
     212
     213    // If the original image's dimensions are over the threshold, scale the image
     214    // and use it as the "full" size.
     215    if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
     216        $editor = wp_get_image_editor( $file );
     217
     218        if ( is_wp_error( $editor ) ) {
     219            // This image cannot be edited.
     220            return $image_meta;
     221        }
     222
     223        // Resize the image
     224        $resized = $editor->resize( $threshold, $threshold );
     225
     226        if ( ! is_wp_error( $resized ) ) {
     227            // TODO: EXIF rotate here.
     228            // By default the editor will append `{width}x{height}` to the file name of the resized image.
     229            // Better to append the threshold size instead so the image file name would be like "my-image-2560.jpg"
     230            // and not look like a "regular" sub-size.
     231            // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
     232            $saved = $editor->save( $editor->generate_filename( $threshold ) );
     233
     234            if ( ! is_wp_error( $saved ) ) {
     235                $new_file = $saved['path'];
     236
     237                // Update the attached file meta.
     238                update_attached_file( $attachment_id, $new_file );
     239
     240                // Width and height of the new image.
     241                $image_meta['width']  = $saved['width'];
     242                $image_meta['height'] = $saved['height'];
     243
     244                // Make the file path relative to the upload dir.
     245                $image_meta['file'] = _wp_relative_upload_path( $new_file );
     246
     247                // Store the original image file name in image_meta.
     248                $image_meta['original_image'] = wp_basename( $file );
     249            }
    189250        }
    190251    }
     
    224285 */
    225286function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) {
     287    if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
     288        // Not an image attachment.
     289        return array();
     290    }
     291
    226292    // Check if any of the new sizes already exist.
    227293    if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
     
    238304    }
    239305
    240     if ( ! empty( $new_sizes ) ) {
    241         // Sort the image sub-sizes in order of priority when creating them.
    242         // This ensures there is an appropriate sub-size the user can access immediately
    243         // even when there was an error and not all sub-sizes were created.
    244         $priority = array(
    245             'medium'       => null,
    246             'large'        => null,
    247             'thumbnail'    => null,
    248             'medium_large' => null,
    249         );
    250 
    251         $new_sizes = array_filter( array_merge( $priority, $new_sizes ) );
    252 
    253         $editor = wp_get_image_editor( $file );
    254 
    255         if ( ! is_wp_error( $editor ) ) {
    256             if ( method_exists( $editor, 'make_subsize' ) ) {
    257                 foreach ( $new_sizes as $new_size_name => $new_size_data ) {
    258                     $new_size_meta = $editor->make_subsize( $new_size_data );
    259 
    260                     if ( is_wp_error( $new_size_meta ) ) {
    261                         $error_code = $new_size_meta->get_error_code();
    262 
    263                         if ( $error_code === 'error_getting_dimensions' ) {
    264                             // Ignore errors when `image_resize_dimensions()` returns false.
    265                             // They mean that the requested size is larger than the original image and should be skipped.
    266                             continue;
    267                         }
    268 
    269                         if ( empty( $image_meta['subsize_errors'] ) ) {
    270                             $image_meta['subsize_errors'] = array();
    271                         }
    272 
    273                         $error = array(
    274                             'error_code'    => $error_code,
    275                             'error_message' => $new_size_meta->get_error_message(),
    276                         );
    277 
    278                         // Store the error code and error message for displaying in the UI.
    279                         $image_meta['subsize_errors'][ $new_size_name ] = $error;
    280                     } else {
    281                         // The sub-size was created successfully.
    282                         // Clear out previous errors in creating this subsize.
    283                         if ( ! empty( $image_meta['subsize_errors'][ $new_size_name ] ) ) {
    284                             unset( $image_meta['subsize_errors'][ $new_size_name ] );
    285                         }
    286 
    287                         if ( empty( $image_meta['subsize_errors'] ) ) {
    288                             unset( $image_meta['subsize_errors'] );
    289                         }
    290 
    291                         // Save the size meta value.
    292                         $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
    293                     }
    294 
    295                     wp_update_attachment_metadata( $attachment_id, $image_meta );
     306    if ( empty( $new_sizes ) ) {
     307        // Nothing to do...
     308        return $image_meta;
     309    }
     310
     311    // Sort the image sub-sizes in order of priority when creating them.
     312    // This ensures there is an appropriate sub-size the user can access immediately
     313    // even when there was an error and not all sub-sizes were created.
     314    $priority = array(
     315        'medium'       => null,
     316        'large'        => null,
     317        'thumbnail'    => null,
     318        'medium_large' => null,
     319    );
     320
     321    $new_sizes = array_filter( array_merge( $priority, $new_sizes ) );
     322
     323    $editor = wp_get_image_editor( $file );
     324
     325    if ( is_wp_error( $editor ) ) {
     326        // The image cannot be edited.
     327        return $image_meta;
     328    }
     329
     330    if ( method_exists( $editor, 'make_subsize' ) ) {
     331        foreach ( $new_sizes as $new_size_name => $new_size_data ) {
     332            $new_size_meta = $editor->make_subsize( $new_size_data );
     333
     334            if ( is_wp_error( $new_size_meta ) ) {
     335                $error_code = $new_size_meta->get_error_code();
     336
     337                if ( $error_code === 'error_getting_dimensions' ) {
     338                    // Ignore errors when `image_resize_dimensions()` returns false.
     339                    // They mean that the requested size is larger than the original image and should be skipped.
     340                    continue;
    296341                }
     342
     343                if ( empty( $image_meta['subsize_errors'] ) ) {
     344                    $image_meta['subsize_errors'] = array();
     345                }
     346
     347                $error = array(
     348                    'error_code'    => $error_code,
     349                    'error_message' => $new_size_meta->get_error_message(),
     350                );
     351
     352                // Store the error code and error message for displaying in the UI.
     353                $image_meta['subsize_errors'][ $new_size_name ] = $error;
    297354            } else {
    298                 // Fall back to `$editor->multi_resize()`.
    299                 $created_sizes = $editor->multi_resize( $new_sizes );
    300 
    301                 if ( ! empty( $created_sizes ) ) {
    302                     $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
     355                // The sub-size was created successfully.
     356                // Clear out previous errors in creating this subsize.
     357                if ( ! empty( $image_meta['subsize_errors'][ $new_size_name ] ) ) {
     358                    unset( $image_meta['subsize_errors'][ $new_size_name ] );
     359                }
     360
     361                if ( empty( $image_meta['subsize_errors'] ) ) {
    303362                    unset( $image_meta['subsize_errors'] );
    304                     wp_update_attachment_metadata( $attachment_id, $image_meta );
    305363                }
    306             }
     364
     365                // Save the size meta value.
     366                $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
     367            }
     368
     369            wp_update_attachment_metadata( $attachment_id, $image_meta );
     370        }
     371    } else {
     372        // Fall back to `$editor->multi_resize()`.
     373        $created_sizes = $editor->multi_resize( $new_sizes );
     374
     375        if ( ! empty( $created_sizes ) ) {
     376            $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
     377            unset( $image_meta['subsize_errors'] );
     378            wp_update_attachment_metadata( $attachment_id, $image_meta );
    307379        }
    308380    }
     
    329401    if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) {
    330402        // Make thumbnails and other intermediate sizes.
    331         $metadata = wp_create_image_subsizes( $file, $metadata, $attachment_id );
     403        $metadata = wp_create_image_subsizes( $file, $attachment_id );
    332404    } elseif ( wp_attachment_is( 'video', $attachment ) ) {
    333405        $metadata = wp_read_video_metadata( $file );
     
    900972    return $dst_file;
    901973}
     974
     975/**
     976 * Retrieves the path to an uploaded image.
     977 *
     978 * Similar to `get_attached_file()` however some images may have been
     979 * processed after uploading to make them "web ready".
     980 * In this case this function returns the path to the originally uploaded image file.
     981 *
     982 * @since 5.3.0
     983 *
     984 * @param int $attachment_id Attachment ID.
     985 * @return string|false Path to the original image file or false if the attachment is not an image.
     986 */
     987function wp_get_original_image_path( $attachment_id ) {
     988    if ( ! wp_attachment_is_image( $attachment_id ) ) {
     989        return false;
     990    }
     991
     992    $image_meta = wp_get_attachment_metadata( $attachment_id );
     993    $image_file = get_attached_file( $attachment_id );
     994
     995    if ( empty( $image_meta['original_image'] ) ) {
     996        $original_image = $image_file;
     997    } else {
     998        $original_image = path_join( dirname( $image_file ), $image_meta['original_image'] );
     999    }
     1000
     1001    /**
     1002     * Filters the path to the original image.
     1003     *
     1004     * @since 5.3.0
     1005     *
     1006     * @param string $original_image Path to original image file.
     1007     * @param int    $attachment_id  Attachment ID.
     1008     */
     1009    return apply_filters( 'wp_get_original_image_path', $original_image, $attachment_id );
     1010}
  • trunk/src/wp-includes/post.php

    r45932 r46076  
    56395639        if ( ! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id ) ) ) {
    56405640            $thumbfile = str_replace( wp_basename( $file ), $meta['thumb'], $file );
     5641
    56415642            if ( ! empty( $thumbfile ) ) {
    56425643                $thumbfile = path_join( $uploadpath['basedir'], $thumbfile );
     
    56535654    if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
    56545655        $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) );
     5656
    56555657        foreach ( $meta['sizes'] as $size => $sizeinfo ) {
    56565658            $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file );
     5659
    56575660            if ( ! empty( $intermediate_file ) ) {
    56585661                $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
     
    56655668    }
    56665669
     5670    if ( ! empty( $meta['original_image'] ) ) {
     5671        if ( empty( $intermediate_dir ) ) {
     5672            $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) );
     5673        }
     5674
     5675        $original_image = str_replace( wp_basename( $file ), $meta['original_image'], $file );
     5676
     5677        if ( ! empty( $original_image ) ) {
     5678            $original_image = path_join( $uploadpath['basedir'], $original_image );
     5679
     5680            if ( ! wp_delete_file_from_directory( $original_image, $intermediate_dir ) ) {
     5681                $deleted = false;
     5682            }
     5683        }
     5684    }
     5685
    56675686    if ( is_array( $backup_sizes ) ) {
    56685687        $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) );
     5688
    56695689        foreach ( $backup_sizes as $size ) {
    56705690            $del_file = path_join( dirname( $meta['file'] ), $size['file'] );
     5691
    56715692            if ( ! empty( $del_file ) ) {
    56725693                $del_file = path_join( $uploadpath['basedir'], $del_file );
Note: See TracChangeset for help on using the changeset viewer.