Каждый пользовательский размер изображения в пользовательском каталоге загрузки?

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 .

Филипп Кюн
источник

Ответы:

21

Филипп, все возможно, если вы решите на это. Вы можете решить свою проблему, расширив класс редактора изображений 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, потому что оно обычно предпочтительнее движка GD по умолчанию в PHP. На большинстве общих серверов расширение 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()и использовать, mb_str_replace()чтобы изменить суффикс на префикс, но я подумал, что это будет более склонно пойти не так.


Сохранение новых путей к метаданным

После загрузки image.jpgпапка для загрузки выглядит следующим образом:

  • 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. Если вы загрузите много небольших изображений, вы получите много разных папок.

Хорошее решение было бы использовать либо пули размера в качестве имени папки ( small, medium, и так далее) или расширить код для круглых размеров до ближайшего предпочтительного размера изображения.

Вы отметили, что хотите использовать только ширину в качестве имени каталога. Будьте осторожны - плагины или темы могут генерировать два разных размера с одинаковой шириной, но разной высоты.

Кроме того, вы можете удалить папки «год / месяц», отключив «Организовать мои загрузки в папки по месяцам и годам» в меню «Настройки»> «Мультимедиа» или generate_filenameеще больше изменив настройки .

Надеюсь это поможет. Удачи!

Robbert
источник
3
Какой ответ! : D Хороший человек!
Филипп Кюн
1
Пожалуйста! Я предполагал, что у вас есть хотя бы небольшой опыт работы с фильтрами ООП и WP, но если есть что-то, чего вы еще не понимаете, не стесняйтесь спрашивать. Спасибо за щедрость!
Роберт
2
@ Роберт Честно говоря, это великолепно. Я рвал на себе волосы из-за отсутствия действий и фильтров в системе загрузки медиа. Оглядываясь назад, это кажется очевидным, но мне просто не пришло в голову полностью переопределить редакторы изображений. Это решает так много проблем одним махом.
Джонатан Финглэнд
1
@JonathanFingland Ха, я признаю, что для этого мне пришлось пройти далеко вниз по кроличьей норе. Рад помочь!
Роберт
Небольшое примечание - в последней части кода (публичная функция multi_resize ($ sizes)) ключевое слово "public" отключает сайт. Просто удалите его, и оно снова. 2k17 и твой ответ все еще классный, спасибо !!
Paradoxetion
3

Ответ Роберта был божественным ресурсом в моих попытках хранить альтернативные размеры, сгенерированные 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

Arty2
источник
1

Я посмотрел на эти части кода WordPress и боюсь, у меня нет хороших новостей.

Есть 2 класса:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick,

оба расширяют абстрактный WP_Image_Editorкласс.

Эти классы реализуют multi_resizeметод, который используется для генерации нескольких изображений из загруженного.

Действительно плохая новость заключается в том, что там нет ловушек фильтров, которые мы могли бы использовать, чтобы изменить путь назначения для вновь создаваемых файлов.

Krzysiek Dróżdż
источник
Это позор. Я хотел реализовать хороший Imager.js, но это, вероятно, не сработает без этого.
Филипп Кюн
Похоже, что Imager.js делает изображения невидимыми для ботов (Google, Facebook и т. Д.), Поэтому я бы посоветовал не использовать их (если вы также не добавляете noscriptтег вручную )
fregante
Хм, нет, я так не думаю. Вы обычно используете img-теги с источником изображения. И добавить дополнительно дата-тег с синтаксисом других образов-размеров: <img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />. А затем скрипт проверяет, какой размер имеет img, и загружает лучший для этого размер изображения.
Филипп Кюн
@ PhilippKühn Я тоже разочарован. Ваша идея была довольно аккуратной, и я хотел использовать ее, чтобы привести в порядок каталог загрузок (удаление неиспользуемых миниатюр после смены темы - боль в ...)
Krzysiek Dróżdż
@ KrzysiekDróżdż Эй, я думаю, что понял. Посмотрите на мой ответ ниже. С помощью этого решения вы также можете сортировать по ftp изображения по имени файла и легко удалять неиспользуемые размеры изображения.
Филипп Кюн
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

Это не возможно добавить вложенный в имена файлов, потому что если я добавлять изображения в записи / страницах всегда первоисточник будет использоваться вместо этого. И удаление этих изображений при удалении также не будет работать. Я не уверен почему.

Филипп Кюн
источник