【问题标题】:WordPress convert image to WebP format programmatically with GD image engineWordPress 使用 GD 图像引擎以编程方式将图像转换为 WebP 格式
【发布时间】:2021-07-14 21:50:06
【问题描述】:

有许多 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 文件就可以在 @987654337 中使用@元素。

  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');
  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 元素和或多或少的以下设置,所以我让浏览器决定需要什么,因此不需要/不需要服务器端.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)WordPress - Blur Image on Upload d)Convert Images into WebP e) 我已通读并理解https://kinsta.com/blog/wordpress-hooks/#filters-example-2-insert-content-after-a-post - 然而我缺少的是一种查看/了解我正在处理的数据的方法,即
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;
}

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

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

【问题讨论】:

    标签: php wordpress filter hook action


    【解决方案1】:

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

    在媒体中,你看不到 webp 图片,所以你不能选择它们。

    【讨论】:

    • 编辑您的帖子后,我只是认为这似乎不是一个答案。如果您有问题,请提出一个新问题,而不是回答另一个问题!
    • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。 - From Review
    【解决方案2】:

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

    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 文件以查找我要查找的内容。

    至于转换,当我看到我正在处理的数据时,我编写了一个类,将上传的图像及其在上传完成后创建的所有大小转换为 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();
    
    }
    

    最后我还添加了一个方法,一旦您选择通过单击媒体库中的“永久删除”来删除文件,它将删除该文件的所有创建的 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' );
    }
    

    谢谢,简!

    【讨论】:

    • 感谢您的课程和代码。我有类似的需求,但我不需要保存原始 PNG,实际上只保留 webp 版本。我认为这是在将图像添加到媒体库之前找到合适的钩子来“交换”图像的问题,但到目前为止我还没有找到它。也许你知道它是哪一个,你能指出我正确的方向吗?谢谢编辑:也许我找到了。 developer.wordpress.org/reference/hooks/…
    猜你喜欢
    • 2019-04-16
    • 2012-01-14
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 2013-11-20
    • 2021-09-18
    相关资源
    最近更新 更多