WordPress 使用 GD 图像引擎以编程方式将图像转换为 WebP 格式

low*_*sun 5 php wordpress hook action filter

有许多 PHP 解决方案和 WP 插件,它们都带有我不想要/不需要的附加选项,即如何提供转换后的文件、存储它们的位置等。

我不需要所有这些,并且正在寻找使用 GD 的纯简单代码。我不想使用插件,谢谢。

  1. 编码应该什么时候发生?任何时候你知道这是钩子例程中的一个好点,可能是这个https://make.wordpress.org/core/2019/11/05/use-of-the-wp_update_attachment_metadata-filter-as-upload- is-complete-hook/但如果您更了解或有其他解决方案,请使用它,并可能让我知道您为什么选择另一个钩子。如果更好的话,一旦上传新图像,我也很乐意触发 cron 作业。另外,我不需要在 WP 数据库中拥有转换后图像的元数据,只要.jpeg在媒体库中拥有原始文件及其元数据就可以了,这些.webp文件只是在元素内部使用picture

  2. 转换后的文件应该存储在哪里?wp-content/uploads/默认文件夹结构,.webp文件应该位于.jpeg所有文件的旁边。

  3. 应使用GD图像引擎进行转换。https://developer.wordpress.org/reference/classes/wp_image_editor_gd/最近我发现 imagick 崩溃或者需要很长时间才能做任何事情。在 WP 5.2 中,imagick 仍然可以正常工作,但一定发生了一些变化,使得在 WP 的更高版本中使用 imagick 毫无用处。我发现 GD 对我来说非常稳定和快速,它创建有损的 WebP 版本并不重要。WP 的 GD 图像引擎的方法似乎不包括转换/编码https://developer.wordpress.org/reference/classes/wp_image_editor_gd/#methods所以我也对 GD 模块中使用的任何方法感到满意https: //www.php.net/manual/en/book.image.php有关 WebP 编码的信息。

  4. 为了摆脱随着时间的推移引入的所有额外不需要的图像尺寸和选项 WP,我在functions.php.

function namespace_disable_image_sizes($sizes)
{
    unset($sizes['thumbnail']);    // disable thumbnail size
    unset($sizes['medium']);       // disable medium size
    unset($sizes['large']);        // disable large size
    unset($sizes['medium_large']); // disable medium-large size
    unset($sizes['1536x1536']);    // disable 2x medium-large size
    unset($sizes['2048x2048']);    // disable 2x large size

    return $sizes;
}
add_action('intermediate_image_sizes_advanced', 'namespace_disable_image_sizes');

// disable scaled image size
add_filter('big_image_size_threshold', '__return_false');

// disable rotated image size
add_filter('wp_image_maybe_exif_rotate', '__return_false');

// disable other image sizes
function namespace_disable_other_image_sizes()
{
    remove_image_size('post-thumbnail'); // disable images added via set_post_thumbnail_size()
    remove_image_size('another-size');   // disable any other added image sizes
}
add_action('init', 'namespace_disable_other_image_sizes');
Run Code Online (Sandbox Code Playgroud)
  1. 需要转换高分辨率和大尺寸图像,请参见附图作为示例,图像类型可以是jpegpng等。

  2. 现有的尺寸或多或少都是这些,可能会有变化。

add_image_size('4096w', 4096, 0);
add_image_size('3200w', 3200, 0);
add_image_size('2560w', 2560, 0);
add_image_size('1920w', 1920, 0);
add_image_size('1600w', 1600, 0);
add_image_size('1280w', 1280, 0);
add_image_size('1140w', 1140, 0);
add_image_size('1024w', 1024, 0);
add_image_size('960w', 960, 0);
add_image_size('800w', 800, 0);
add_image_size('768w', 768, 0);
add_image_size('640w', 640, 0);
add_image_size('425w', 425, 0);
add_image_size('320w', 320, 0);
add_image_size('240w', 240, 0);
Run Code Online (Sandbox Code Playgroud)
  1. 我使用的picture元素或多或少具有以下设置,因此我让浏览器决定需要什么,因此不需要/不需要服务器端.htaccess规则或后端配置。https://dev.opera.com/articles/responsive-images/
<picture>
    <source
        sizes="(min-width: 640px) 60vw, 100vw"
        srcset="opera-200.webp 200w,
                opera-400.webp 400w,
                opera-800.webp 800w,
                opera-1200.webp 1200w,
                opera-1600.webp 1600w,
                opera-2000.webp 2000w"
        type="image/webp">
    <img
        src="opera-400.jpg" alt="The Oslo Opera House"
        sizes="(min-width: 640px) 60vw, 100vw"
        srcset="opera-200.jpg 200w,
                opera-400.jpg 400w,
                opera-800.jpg 800w,
                opera-1200.jpg 1200w,
                opera-1600.jpg 1600w,
                opera-2000.jpg 2000w">
</picture>
Run Code Online (Sandbox Code Playgroud)
  1. 我尝试了什么?a) https://wordpress.stackexchange.com/questions/256351/hook-after-image-is-uploaded-and-image-sizes- generated/256352 b) https://wordpress.stackexchange.com/questions/38582 /hook-to-get-image-filename-when-it-is-uploaded c) WordPress - 上传时模糊图像 d)将图像转换为 WebP e) 我已阅读并理解https://kinsta.com/blog/ wordpress-hooks/#filters-example-2-insert-content-after-a-post -但是我缺少的是一种查看/知道我正在使用哪些数据的方法,即
<picture>
    <source
        sizes="(min-width: 640px) 60vw, 100vw"
        srcset="opera-200.webp 200w,
                opera-400.webp 400w,
                opera-800.webp 800w,
                opera-1200.webp 1200w,
                opera-1600.webp 1600w,
                opera-2000.webp 2000w"
        type="image/webp">
    <img
        src="opera-400.jpg" alt="The Oslo Opera House"
        sizes="(min-width: 640px) 60vw, 100vw"
        srcset="opera-200.jpg 200w,
                opera-400.jpg 400w,
                opera-800.jpg 800w,
                opera-1200.jpg 1200w,
                opera-1600.jpg 1600w,
                opera-2000.jpg 2000w">
</picture>
Run Code Online (Sandbox Code Playgroud)

不会向我显示任何内容,与尝试登录到控制台或插件文件夹中的文件相同。在不知道/看到数据以及哪些变量名包含哪些数据的情况下,我只是在进行试验、错误和猜测,没有编码。因此,给出上面的代码,首先如何才能查看/知道哪些变量在该时间点保存哪些数据并使其在某个地方可读,即在插件文件夹中的日志文件中?

底线是,上面的设置帮助我理解哪些变量保存哪些数据,即在挂钩中上传后,并包含代码,我可以在其中制作所有尺寸的 WebP 版本以及使用 GD 图像引擎创建的原始版本。

上传文件夹中的 JPEG 和 WebP 图像彼此相邻

示例图像

low*_*sun 1

要了解您在过滤器或操作中使用哪些数据,并查看哪些变量名称包含哪些值,可以使用如下所示的辅助函数。

function debug( $info ) {
    $message = null;

    if ( is_string( $info ) || is_int( $info ) || is_float( $info ) ) {
        $message = $info;
    } else {
        $message = var_export( $info, true );
    }

    if ( $fh = fopen( ABSPATH . '/gdwebpconvert.log', 'a' ) ) {
        fputs( $fh, date( 'Y-m-d H:i:s' ) . " $message\n" );
        fclose( $fh );
    }
}
Run Code Online (Sandbox Code Playgroud)

这将在 WordPress 安装的根目录中创建一个gdwebpconvert.log文件,您放入的任何字符串、整数、浮点或数组都debug($value_xyz);将连同日期和时间一起记录到该文件中。在 Linux 上,您可以转到保存该文件的目录并执行以下操作tail -f gdwebpconvert.log,该文件的最新条目将显示在终端中。

作为替代方案,您可以通过将这些行添加到wp-config.php.

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
Run Code Online (Sandbox Code Playgroud)

这将导致信息输出到文件中debug.log,也在 WordPress 安装的根目录中,尽管它也会一直在其中添加大量数据。因此,我更喜欢上面的小辅助函数来获取我当前想要查看的值,而不必在文件中搜索debug.log我要查找的内容。

至于转换,当我看到我正在使用的数据时,我编写了一个类,在上传完成后将上传的图像及其所有创建的尺寸转换为 WebP 格式。继续取消debug()注释后面的语句。鉴于此https://github.com/Imagick/imagick/issues/358以及我自 WordPress 5.2 以来使用 Imagick 描述的问题,这提供了一个简单的解决方案,只需在服务器上创建文件的 WebP 版本,然后您可以在任何地方使用您喜欢的方式,无需添加自动.htaccess或其他功能。

随意用它来做你想要和需要的事情。;)

<?php
/**
* Plugin Name: GD WebP Converter
* Plugin URI: https://stackoverflow.com/a/67234000
* Description: After uploading an image it will be converted to WebP format using the GD image engine. <a target="_blank" href="https://developer.wordpress.org/reference/classes/wp_image_editor_gd/">WP GD Image Engine</a> If the file is deleted form the Media Library the created WebP conversions will also be deleted.
* Version: 1.0.0
* Requires at least: 5.5
* Requires PHP: 7.2
* Author: lowtechsun
* Author URI: https://stackoverflow.com/users/1010918/lowtechsun
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

//=================================================
// Security: Abort if this file is called directly
//=================================================
if ( ! defined( 'ABSPATH' ) ) {
    die;
}

function debug( $info ) {
    $message = null;

    if ( is_string( $info ) || is_int( $info ) || is_float( $info ) ) {
        $message = $info;
    } else {
        $message = var_export( $info, true );
    }

    if ( $fh = fopen( ABSPATH . '/gdwebpconvert.log', 'a' ) ) {
        fputs( $fh, date( 'Y-m-d H:i:s' ) . " $message\n" );
        fclose( $fh );
    }
}

add_filter( 'wp_generate_attachment_metadata', 'gd_webp_converter', 10, 2 );

function gd_webp_converter( $metadata, $attachment_id ) {

    $gd_webp_converter = new GDWebPConverter( $attachment_id );
    $gd_webp_converter->check_file_exists( $attachment_id );
    $gd_webp_converter->check_mime_type();
    $gd_webp_converter->create_array_of_sizes_to_be_converted( $metadata );
    $gd_webp_converter->convert_array_of_sizes();

    return $metadata;
}

class GDWebPConverter {

    private $file_path;
    private $file_dirname;
    private $file_ext;
    private $file_name_no_ext;

    private $array_of_sizes_to_be_converted = array();
    private $array_of_sizes_to_be_deleted   = array();

    public function __construct( $attachment_id ) {

        $this->file_path = get_attached_file( $attachment_id );
        debug( $this->file_path );

        // https://stackoverflow.com/questions/2183486/php-get-file-name-without-file-extension/19040276
        $this->file_dirname = pathinfo( $this->file_path, PATHINFO_DIRNAME );
        debug( $this->file_dirname );

        $this->file_ext = strtolower( pathinfo( $this->file_path, PATHINFO_EXTENSION ) );
        debug( $this->file_ext );

        $this->file_name_no_ext = pathinfo( $this->file_path, PATHINFO_FILENAME );
        debug( $this->file_name_no_ext );
    }

    public function check_file_exists( $attachment_id ) {

        $file = get_attached_file( $attachment_id );

        if ( ! file_exists( $file ) ) {
            $message = 'The uploaded file does not exist on the server. Encoding not possible.';
            debug( $message );
            throw new Exception( 'The uploaded file does exist on the server. Encoding not possible.', 1 );
        }

    }

    public function check_mime_type() {

        // https://www.php.net/manual/en/function.finfo-file.php
        $finfo = finfo_open( FILEINFO_MIME_TYPE );

        $this->file_mime_type = finfo_file( $finfo, $this->file_path );

        finfo_close( $finfo );
        // debug( $this->file_mime_type );

        // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
        $this->allowed_mime_type = array( 'image/jpeg', 'image/png' );

        if ( ! in_array( $this->file_mime_type, $this->allowed_mime_type, true ) ) {

            $message = 'MIME type of file not supported';
            // debug( $message );
            throw new Exception( 'MIME type of file not supported', 1 );

        }
    }

    public function create_array_of_sizes_to_be_converted( $metadata ) {

        // push original file to the array
        array_push( $this->array_of_sizes_to_be_converted, $this->file_path );
        // debug( $this->array_of_sizes_to_be_converted );

        // push all created sizes of the file to the array
        foreach ( $metadata['sizes'] as $value ) {
            // debug( $value['file'] );
            array_push( $this->array_of_sizes_to_be_converted, $this->file_dirname . '/' . $value['file'] );
        }
        // // debug( $this->array_of_sizes_to_be_converted );
    }

    public function convert_array_of_sizes() {

        debug( $this->array_of_sizes_to_be_converted );

        switch ( $this->file_ext ) {
            case 'jpeg':
            case 'jpg':
                foreach ( $this->array_of_sizes_to_be_converted as $key => $value ) {

                    $image = imagecreatefromjpeg( $value );

                    if ( 0 === $key ) {

                        imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '.webp', 80 );

                    } else {

                        $current_size = getimagesize( $value );
                        // debug( $current_size );
                        imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '-' . $current_size[0] . 'x' . $current_size[1] . '.webp', 80 );

                    }

                    imagedestroy( $image );
                }
                break;

            case 'png':
                foreach ( $this->array_of_sizes_to_be_converted as $key => $value ) {

                    $image = imagecreatefrompng( $value );
                    imagepalettetotruecolor( $image );
                    imagealphablending( $image, true );
                    imagesavealpha( $image, true );

                    if ( 0 === $key ) {

                        imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '.webp', 80 );

                    } else {

                        $current_size = getimagesize( $value );
                        // debug( $current_size );
                        imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '-' . $current_size[0] . 'x' . $current_size[1] . '.webp', 80 );

                    }

                    imagedestroy( $image );

                }
                break;

            // animated GIF to WebP not supported by GD - imagecreatefromgif
            // case 'gif':
            //  foreach ( $this->array_of_sizes_to_be_converted as $key => $value ) {

            //      $image = imagecreatefromgif( $value );

            //      if ( 0 === $key ) {

            //          imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '.webp', 80 );

            //      } else {

            //          $current_size = getimagesize( $value );
            //          // debug( $current_size );
            //          imagewebp( $image, $this->file_dirname . '/' . $this->file_name_no_ext . '-' . $current_size[0] . 'x' . $current_size[1] . '.webp', 80 );

            //      }

            //      imagedestroy( $image );

            //  }
            //  break;

            default:
                return false;
        }

    }

    public function create_array_of_sizes_to_be_deleted( $attachment_id ) {

        // debug( $attachment_id );

        $this->attachment_metadata_of_file_to_be_deleted = wp_get_attachment_metadata( $attachment_id );
        // debug( $this->attachment_metadata_of_file_to_be_deleted );

        // push original file to the array
        array_push( $this->array_of_sizes_to_be_deleted, $this->file_dirname . '/' . $this->file_name_no_ext . '.webp' );
        // debug( $this->array_of_sizes_to_be_converted );

        // push all created sizes of the file to the array
        foreach ( $this->attachment_metadata_of_file_to_be_deleted['sizes'] as $value ) {

            // debug( $value );

            $this->value_file_name_no_ext = pathinfo( $value['file'], PATHINFO_FILENAME );
            // debug( $this->value_file_name_no_ext );

            array_push( $this->array_of_sizes_to_be_deleted, $this->file_dirname . '/' . $this->value_file_name_no_ext . '.webp' );
        }
        // debug( $this->array_of_sizes_to_be_deleted );
    }

    public function delete_array_of_sizes() {

        debug( $this->array_of_sizes_to_be_deleted );

        foreach ( $this->array_of_sizes_to_be_deleted as $key => $value ) {

            // debug( $value );
            unlink( $value );

        }
    }

}

add_action( 'delete_attachment', 'delete_webp_conversions', 10 );

function delete_webp_conversions( $attachment_id ) {

    $delete_webp_conversions = new GDWebPConverter( $attachment_id );
    $delete_webp_conversions->create_array_of_sizes_to_be_deleted( $attachment_id );
    $delete_webp_conversions->delete_array_of_sizes();

}
Run Code Online (Sandbox Code Playgroud)

最后我还添加了一种方法,一旦您选择通过单击媒体库中的“永久删除”来删除文件,它将删除该文件的所有创建的 WebP 版本。如果您删除帖子,则不会发生这种情况WordPress 的默认行为,因为您永远不知道您是否可能需要其他帖子中的文件。

如果您想充分利用此类,请确保默认使用GD 图像编辑器。=> https://support.pagely.com/hc/en-us/articles/115000052451

<?php
/**
* Plugin Name: Use GD For Image Processing
* Plugin URI: https://support.pagely.com/hc/en-us/articles/115000052451
* Description: Sets GD to the default image processor.
* Version: 1.0.0
* Requires at least: 5.5
* Requires PHP: 7.2
* Author: JeffMatson, Pagely
* Author URI: https://pagely.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

add_filter( 'wp_image_editors', 'pagely_default_to_gd' );
function pagely_default_to_gd() {
    return array( 'WP_Image_Editor_GD', 'WP_Image_Editor_Imagick' );
}
Run Code Online (Sandbox Code Playgroud)

谢谢,简!