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

WordPress convert image to WebP format programmatically with GD image engine

有很多 PHP 解决方案和 WP 插件,它们都带有我没有 want/need 的附加选项,即转换文件的服务方式、存储位置等。

我需要 none 所有这些,并且正在寻找使用 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/ Lately I find imagick just crashes or takes ages to do anything. In WP 5.2 things still worked just fine with imagick but there must have been changes introduced that make using imagick in later versions of WP useless. I find GD to me quite stable and fast, it does not matter it creates lossy WebP versions. The methods for the GD image engine from WP do not seem to include conversion/encoding https://developer.wordpress.org/reference/classes/wp_image_editor_gd/#methods so I am also happy with any methods using in the GD module https://www.php.net/manual/en/book.image.php 关于 WebP 的编码。

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

    中有这些 functions/filters
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');
  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);
  1. 我使用 picture 元素或多或少有以下设置,所以我让浏览器决定需要什么,因此不 want/need 服务器端 .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>
  1. 我尝试了什么? 一)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) d) Convert Images into WebP e) 我已经通读并理解 https://kinsta.com/blog/wordpress-hooks/#filters-example-2-insert-content-after-a-post - 但是 我缺少的是 see/know 什么数据的方法我正在与
add_filter('wp_generate_attachment_metadata', 'gd_webp_encode', 10, 3);
function gd_webp_encode($metadata, $attachment_id, $context){
    ob_start();
    echo $attachment_id;
    echo $metadata;
    ob_end_clean();
    return $metadata;
}

不会显示任何内容,就像尝试登录到控制台或插件文件夹中的文件一样。没有 knowing/seeing 数据和什么变量名保存什么数据我只是在试错和猜测,没有编码。所以给出上面的代码,首先如何 see/know 什么变量在那个时间点保存什么数据并使它在某个地方可读,即在插件文件夹的日志文件中?

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

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

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 );
    }
}

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

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

来使用 WordPress 自己的调试功能
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);

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

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

请随意使用您想要和需要的东西。 ;)

<?php
/**
* Plugin Name: GD WebP Converter
* Plugin URI: 
* 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://whosebug.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 );

        // 
        $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();

}

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

中的文件

如果您想充分利用此class,请确保默认 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' );
}

谢谢,简!

两个插件都运行良好(在上传目录中创建 webp 图像),但我想知道如何使用 wp 函数调用 webp 图像(get_the_post_thumbnail_urlwp_get_attachment_url)。

在媒体中,您看不到 webp 图片,因此您不能 select 它们。