問題描述

我想在自定義文件夾中上傳自定義圖片大小。該文件夾應具有所選寬度的名稱。例如:

如果我添加這些自定義尺寸…

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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。