问题描述

我想在自定义文件夹中上传自定义图片大小。该文件夹应具有所选宽度的名称。例如:

如果我添加这些自定义尺寸…

add_image_size('custom-1', 300, 9999);
add_image_size('custom-2', 400, 9999);

这将是很好的上传的图像是这样上传的:

http://www.my-site.com/wp-content/uploads/300/my-image.jpg
http://www.my-site.com/wp-content/uploads/400/my-image.jpg

这可能吗?我只发现我可以用 upload_dir 过滤器更改全局上传文件夹。

最佳解决方案

菲利普,任何事情都是可能的,如果你把你的想法。您可以通过扩展 WordPress 图像编辑器类来解决您的问题。

注意我正在使用 WordPress 3.7 – 我没有在早期版本和最新的 3.8 版本中检查下列代码。

图像编辑器基础知识

WordPress 有两个内置的类来处理图像处理:

  • WP_Image_Editor_GD(/wp-includes/class-wp-image-editor-gd.php)

  • WP_Image_Editor_Imagick(/wp-includes/class-wp-image-editor-imagick.php)

这两个类扩展了 WP_Image_Editor,因为它们都使用不同的图像引擎 (分别为 GD 和 ImageMagick) 来加载,调整大小,压缩和保存图像。

默认情况下,WordPress 将首先尝试使用 ImageMagick 引擎,该引擎需要一个 PHP 扩展,因为它通常优于 PHP 的默认 GD 引擎。大多数共享服务器没有启用 ImageMagick 扩展。

添加图像编辑器

要决定使用哪种引擎,WordPress 会调用内部函数__wp_image_editor_choose()(位于/wp-includes/media.php 中) 。此函数循环遍历所有引擎,以查看哪个引擎可以处理请求。

该功能还有一个称为 wp_image_editors 的过滤器,可以让您添加更多的图像编辑器,如:

add_filter("wp_image_editors", "my_wp_image_editors");
function my_wp_image_editors($editors) {
    array_unshift($editors, "WP_Image_Editor_Custom");

    return $editors;
}

请注意,我们预设了自定义图像编辑器类 WP_Image_Editor_Custom,因此 WordPress 将在测试其他引擎之前检查我们的引擎是否可以处理调整大小。

创建我们的图像编辑器

现在我们要写自己的图像编辑器,所以我们可以自己决定文件名。 Filenaming 由 WP_Image_Editor::generate_filename()(两个引擎继承此方法) 的方法处理,因此我们应该在我们的自定义类中覆盖。

由于我们只计划更改文件名,所以我们应该扩展现有的引擎之一,所以我们不必重新发明。我将在我的例子中扩展 WP_Image_Editor_GD,因为您可能没有启用 ImageMagick 扩展。该代码对于 ImageMagick 设置可以互换。如果您计划使用不同设置的主题,您可以添加两者。

// Include the existing classes first in order to extend them.
require_once ABSPATH.WPINC."/class-wp-image-editor.php";
require_once ABSPATH.WPINC."/class-wp-image-editor-gd.php";

class WP_Image_Editor_Custom extends WP_Image_Editor_GD {
    public function generate_filename($prefix = NULL, $dest_path = NULL, $extension = NULL) {
        // If empty, generate a prefix with the parent method get_suffix().
        if(!$prefix)
            $prefix = $this->get_suffix();

        // Determine extension and directory based on file path.
        $info = pathinfo($this->file);
        $dir  = $info['dirname'];
        $ext  = $info['extension'];

        // Determine image name.
        $name = wp_basename($this->file, ".$ext");

        // Allow extension to be changed via method argument.
        $new_ext = strtolower($extension ? $extension : $ext);

        // Default to $_dest_path if method argument is not set or invalid.
        if(!is_null($dest_path) && $_dest_path = realpath($dest_path))
            $dir = $_dest_path;

        // Return our new prefixed filename.
        return trailingslashit($dir)."{$prefix}/{$name}.{$new_ext}";
    }
}

上面的代码大部分是从 WP_Image_Editor 类直接复制的,并为了方便起见。唯一的实际变化是后缀现在是前缀。

或者,您可以调用 parent::generate_filename()并使用 mb_str_replace()将后缀更改为前缀,但我认为更倾向于出错。

保存元数据的新路径

上传 image.jpg 后,uploads 文件夹如下所示:

  • 2013/12/150x150/image.jpg

  • 2013/12/300x300/image.jpg

  • 2013/12/image.jpg

到现在为止还挺好。但是,当调用 wp_get_attachment_image_src()这样的基本功能时,我们会注意到所有的图像大小都存储为 image.jpg,而没有新的目录路径。

我们可以通过将新的文件夹结构保存到图像元数据 (存储文件名的位置) 来解决此问题。在插入数据库之前,数据通过各种过滤器 (wp_generate_attachment_metadata 等) 运行,但由于我们已经在实现自定义图像编辑器,因此我们可以回到图像大小元数据源 WP_Image_Editor::multi_resize()。它生成像这样的数组:

Array (
    [thumbnail] => Array (
        [file]      => image.jpg
        [width]     => 150
        [height]    => 150
        [mime-type] => image/jpeg
    )

    [medium] => Array (
        [file]      => image.jpg
        [width]     => 300
        [height]    => 300
        [mime-type] => image/jpeg
    )
)

我们将在我们的自定义类中覆盖 multi_resize()方法:

function multi_resize($sizes) {
    $sizes = parent::multi_resize($sizes);

    foreach($sizes as $slug => $data)
        $sizes[$slug]['file'] = $data['width']."x".$data['height']."/".$data['file'];

    return $sizes;
}

你可以看到,我没有打扰替换任何代码。我只是调用父方法并让它生成元数据。然后我循环遍历结果数组,并调整每个大小的 file 值。

现在 wp_get_attachment_image_src($att_id, array(300, 300))返回 2013/12/300x300/image.jpg 。万岁!

最后的想法

我希望这为您提供一个很好的依据。但是,请注意,如果图像小于指定尺寸 (例如,280×300),生成的后缀 (我们的前缀) 和图像尺寸是 280×300,而不是 300×300 。如果你上传了很多较小的图像,你会得到很多不同的文件夹。

一个很好的解决方案是使用大小插件作为文件夹名称 (smallmedium 等),或将代码扩展到圆形大小,直到最接近的首选图像大小。

你注意到你只想使用宽度作为目录名。要警告 – 插件或主题可以生成两个不同的大小,宽度相同但不同的高度。

此外,您可以通过在 「设置」 和 「设置」 下禁用 「将我上传到月份和 year-based 文件夹」 媒体还是进一步操纵 generate_filename

希望这可以帮助。祝你好运!

次佳解决方案

@ Robbert 的答案是将 WordPress 生成的替代大小存储在单独的目录中的神圣资源。我的代码也将上传目录更改为./media,所以如果不想要,请确保编辑这些行。这不是第一个海报问题的确切答案,而是为同一个问题提供了一个替代解决方案:

if ( !is_multisite() ) {
    update_option( 'upload_path', 'media' ); //to-do: add to options page
    define( 'UPLOADS', 'media' ); //define UPLOADS dir - REQUIRED
}
//don't 「Organize my uploads into month- and year-based folders」
update_option( 'uploads_use_yearmonth_folders', '0' ); // to-do: add to options page

//create a custom WP_Image_Editor that handles the naming of files
function tect_image_editors($editors) {
    array_unshift( $editors, 'WP_Image_Editor_tect' );

    return $editors;
}

add_filter( 'wp_image_editors', 'tect_image_editors' );

require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';

class WP_Image_Editor_tect extends WP_Image_Editor_GD {
    public function multi_resize($sizes) {
        $sizes = parent::multi_resize($sizes);

        $media_dir = trailingslashit( ABSPATH . UPLOADS );

        foreach($sizes as $slug => $data) {
            $default_name = $sizes[ $slug ]['file'];
            $new_name = $slug . '/' . preg_replace( '#-d+xd+.#', '.', $data['file'] );

            if ( !is_dir( $media_dir . $slug ) ) {
                mkdir( $media_dir . $slug );
            }
            //move the thumbnail - perhaps not the smartest way to do it...
            rename ( $media_dir . $default_name, $media_dir . $new_name );

            $sizes[$slug]['file'] = $new_name;
        }

        return $sizes;
    }
}

根据我的测试,没有任何问题的工作,虽然我没有试图检查它如何票价与流行的画廊/媒体插件。

相关奖金:一个原始实用程序删除所有 WordPress 生成的缩略图 delete_deprecated_thumbs.php

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。