自定义上传目录中的每个自定义图片大小?


11

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

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

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过滤器更改全局上传文件夹。

Answers:


21

Philipp,只要您下定决心,一切皆有可能。您可以通过扩展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将在测试其他引擎之前检查我们的引擎是否可以处理调整大小。


创建我们的图像编辑器

现在我们要编写自己的图像编辑器,以便我们自己决定文件名。文件命名是由方法处理的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()并使用an 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。万岁!


最后的想法

希望这为您详细阐述奠定了良好的基础。但是,请注意,如果图像小于指定的大小(例如280x300),则生成的后缀(在我们的示例中为前缀)和图像大小为280x300,而不是300x300。如果您上传了很多较小的图片,则会得到很多不同的文件夹。

一个好的解决办法是要么使用大小蛞蝓作为文件夹名(smallmedium,等等)或扩展代码来圆尺寸到最接近的首选的图像尺寸。

您注意到您只想使用宽度作为目录名。不过请注意-插件或主题可能会生成两个大小相同但宽度相同但高度不同的大小。

另外,您可以通过在“设置”>“媒体”下禁用“将我的上传内容整理到基于月和年的文件夹中”或generate_filename进一步进行操作来删除年/月文件夹。

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


3
真是个答案!:D好男人!
菲利普·库恩(PhilippKühn)2013年

1
别客气!我以为您至少具有一点OOP和WP过滤器的经验,但是如果您尚不了解任何内容,请随时提出。谢谢您的悬赏!
罗伯特2013年

2
@罗伯特·坦率地说,这太棒了。由于缺少媒体上载系统中的动作和筛选器挂钩,我被撕碎了。回想起来似乎很明显,但是我完全没有完全覆盖图像编辑器。这样做可以一举解决许多问题。
Jonathan Fingland 2014年

1
@JonathanFingland哈,我承认我必须走到兔子洞的深处。很高兴有帮助!
罗伯特2014年

小提示-在最后一个代码安全性(公共功能multi_resize($ sizes))中,关键字“ public”使网站瘫痪。只需将其删除,它就会再次出现。2k17,您的回答仍然很棒,谢谢!!
悖论

3

@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+x\d+\.#', '.', $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


1

我看过WordPress代码的这些部分,恐怕我没有任何好消息。

有2个类别:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick

都扩展了抽象WP_Image_Editor类。

这些类是实现multi_resize方法,用于从上传的图像生成多个图像。

真正的坏消息是那里没有过滤器挂钩,我们可以使用它来修改新创建文件的目标路径。


真可惜 我想实现漂亮的Imager.js,但如果没有它,那可能行不通。
PhilippKühn'13

看起来Imager.js会使机器人(Google,Facebook等)看不到图像,因此,建议您不要使用它(除非您也手动添加noscript标签)
fregante

嗯,不,我不这么认为。通常,将img-tags与图像的src一起使用。并且您还添加了一个数据标签,其语法为其他图像大小:<img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />。然后,脚本检查img具有的大小,并为此加载最佳图像大小。
PhilippKühn'13

@PhilippKühn我也很失望。您的想法很简洁,我想用它来整理上传目录(在更改主题后删除未使用的缩略图会很痛苦...)
KrzysiekDróżdż

@KrzysiekDróżdż嘿,我想我明白了。看下面我的回答。使用此解决方案,您还可以通过ftp按文件名对图像进行排序,并轻松删除未使用的图像大小。
菲利普·库恩(PhilippKühn)2013年

1

好吧,我想我明白了!不完美,但是还可以,我想要它。对我来说,只有图像的宽度很重要。身高对我没用。特别是对于实现Imager.js而言,图像URL中的高度令人不安。

add_filter('image_make_intermediate_size', 'custom_rename_images');

function custom_rename_images($image) {
    // Split the $image path
    $info = pathinfo($image);
    $dir = $info['dirname'] . '/';
    $ext = '.' . $info['extension'];
    $name = wp_basename($image, '$ext');

    // New Name
    $name_prefix = substr($name, 0, strrpos($name, '-'));
    $size_extension = substr($name, strrpos($name, '-') + 1);
    $image_sizes = explode('x', $size_extension);
    $image_width = $image_sizes[0];
    $new_name = $dir . $image_width . '-' . $name_prefix . $ext;

    // Rename the intermediate size
    $did_it = rename($image, $new_name);

    // Return if successful
    if ($did_it) return $new_name;

    // Return on fail
    return $image;
}

使用此代码,文件名如下:

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

这是不是可以将子文件夹添加到文件名,因为如果我在文章/页面添加图像总是原始来源将改为使用。并且在删除时删除这些图像也将不起作用。我不知道为什么。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.