¿Cada tamaño de imagen personalizado en el directorio de carga personalizado?

11

Quiero cargar mis tamaños de imagen personalizados en carpetas personalizadas. La carpeta debe tener el nombre del ancho seleccionado. Por ejemplo:

Si agrego estos tamaños personalizados ...

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

Sería bueno que las imágenes cargadas se carguen así:

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

es posible? Solo he descubierto que puedo cambiar la carpeta de carga global con el filtro upload_dir .

Philipp Kühn
fuente

Respuestas:

21

Philipp, todo es posible si te lo propones. Puede resolver su problema extendiendo la clase del editor de imágenes de WordPress.

Tenga en cuenta que estoy usando WordPress 3.7: no he verificado ninguno de los siguientes códigos en versiones anteriores y en la última versión 3.8.


Conceptos básicos del editor de imágenes

WordPress tiene dos clases integradas que manejan la manipulación de imágenes:

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

Estas dos clases se extienden WP_Image_Editorporque ambas usan un motor de imagen diferente (GD e ImageMagick respectivamente) para cargar, redimensionar, comprimir y guardar imágenes.

Por defecto, WordPress intentará usar primero el motor ImageMagick, que necesita una extensión PHP, porque generalmente se prefiere sobre el motor GD predeterminado de PHP. Sin embargo, la mayoría de los servidores compartidos no tienen habilitada la extensión ImageMagick.


Agregar un editor de imágenes

Para decidir qué motor usar, WordPress llama a una función interna __wp_image_editor_choose()(ubicada en /wp-includes/media.php). Esta función recorre todos los motores para ver qué motor puede manejar la solicitud.

La función también tiene un filtro llamado wp_image_editorsque le permite agregar más editores de imágenes de esta manera:

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

    return $editors;
}

Tenga en cuenta que estamos anteponiendo nuestra clase de editor de imágenes personalizado WP_Image_Editor_Custompara que WordPress verifique si nuestro motor puede manejar el cambio de tamaño antes de probar otros motores.


Creando nuestro editor de imágenes

Ahora vamos a escribir nuestro propio editor de imágenes para que podamos decidir sobre los nombres de archivo por nosotros mismos. Filenaming se maneja mediante el método WP_Image_Editor::generate_filename()(ambos motores heredan este método), por lo que deberíamos sobrescribirlo en nuestra clase personalizada.

Dado que solo planeamos cambiar los nombres de los archivos, deberíamos extender uno de los motores existentes para no tener que reinventar la rueda. Ampliaré WP_Image_Editor_GDen mi ejemplo, ya que probablemente no tenga habilitada la extensión ImageMagick. Sin embargo, el código es intercambiable para una configuración de ImageMagick. Puede agregar ambos si está planeando usar el tema en diferentes configuraciones.

// 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}";
    }
}

La mayor parte del código anterior se copió directamente de la WP_Image_Editorclase y se comentó para su conveniencia. El único cambio real es que el sufijo ahora es un prefijo.

Alternativamente, puede llamar parent::generate_filename()y usar un mb_str_replace()para cambiar el sufijo en un prefijo, pero pensé que sería más probable que saliera mal.


Guardar nuevas rutas a metadatos

Después de cargar image.jpg, la carpeta de cargas se ve así:

  • 2013/12/150x150/image.jpg
  • 2013/12/300x300/image.jpg
  • 2013/12/image.jpg

Hasta aquí todo bien. Sin embargo, al llamar a funciones básicas como wp_get_attachment_image_src(), notaremos que todos los tamaños de imagen se almacenan image.jpgsin la nueva ruta de directorio.

Podemos solucionar este problema guardando la nueva estructura de carpetas en los metadatos de la imagen (donde se almacenan los nombres de archivo). Las ejecuciones de datos a través de diferentes filtros ( wp_generate_attachment_metadataentre otros) antes de insertarse en la base de datos, pero puesto que ya estamos implementando un editor de imagen personalizada, que pueden viajar de regreso a la fuente de metadatos de la imagen tamaño: WP_Image_Editor::multi_resize(). Genera matrices como esta:

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
    )
)

Sobreescribiremos el multi_resize()método en nuestra clase personalizada:

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;
}

Como puede ver, no me molesté en reemplazar ninguno de los códigos. Solo llamo al método padre y dejo que genere los metadatos. Luego recorro la matriz resultante y ajusto el filevalor para cada tamaño.

Ahora wp_get_attachment_image_src($att_id, array(300, 300))vuelve 2013/12/300x300/image.jpg. ¡Hurra!


Pensamientos finales

Espero que esto haya sido una buena base para que profundices. Sin embargo, tenga en cuenta que si una imagen es más pequeña que el tamaño especificado (por ejemplo, 280x300), el sufijo generado (prefijo en nuestro caso) y los tamaños de imagen son 280x300, no 300x300. Si carga muchas imágenes más pequeñas, obtendrá muchas carpetas diferentes.

Una buena solución sería la de utilizar la babosa tamaño que un nombre de la carpeta ( small, medium, etc.) o ampliar el código para tamaños redondos hasta el tamaño de la imagen preferida más cercano.

Notó que desea usar solo el ancho como nombre de directorio. Sin embargo, tenga en cuenta: los complementos o temas podrían generar dos tamaños diferentes con el mismo ancho pero diferentes alturas.

Además, puede eliminar las carpetas de año / mes desactivando 'Organizar mis cargas en carpetas basadas en mes y año' en Configuración> Medios o manipulando generate_filenameaún más.

Espero que esto ayude. ¡Buena suerte!

Robbert
fuente
3
¡Qué respuesta! : D ¡Buen hombre!
Philipp Kühn
1
¡De nada! Supuse que tienes al menos un poco de experiencia en filtros de OOP y WP, pero si hay algo que aún no entiendes, no dudes en preguntar. ¡Gracias por la recompensa!
Robbert
2
@Robbert Francamente, esto es brillante. Me estaba arrancando el pelo por la falta de acción y los ganchos de filtro en el sistema de carga de medios. Parece obvio en retrospectiva, pero simplemente no se me ocurrió anular por completo los editores de imágenes. Hacerlo resuelve tantos problemas de una sola vez.
Jonathan Fingland
1
@ JonathanFingland Ja, admito que tuve que ir muy lejos por la madriguera del conejo para este. ¡Me alegra ser de ayuda!
Robbert
Pequeña nota: en última instancia, la palabra clave "public" de public function multi_resize ($ tamaños) hace que el sitio web caiga Simplemente quítelo y vuelve a funcionar. 2k17 y tu respuesta sigue siendo increíble, ¡gracias!
Paradoxetion
3

La respuesta de @ Robbert fue un recurso divino en mis esfuerzos por almacenar tamaños alternativos generados por WordPress en directorios separados. Mi código también cambia el directorio de carga a ./media, así que asegúrese de editar estas líneas si no lo desea. No es una respuesta exacta a la pregunta del primer póster, pero ofrece una solución alternativa al mismo problema:

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;
    }
}

Funciona sin problemas de acuerdo con mis pruebas, aunque no he intentado comprobar cómo le va con los populares complementos de galería / medios.

bonificación relacionada: una utilidad sin formato para eliminar todas las miniaturas generadas por WordPress delete_deprecated_thumbs.php

Arty2
fuente
1

He visto estas partes del código de WordPress y me temo que no tengo buenas noticias.

Hay 2 clases:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick,

ambos extendiendo la WP_Image_Editorclase abstracta .

Estas clases son multi_resizemétodos de implementación , que se utilizan para generar múltiples imágenes desde una cargada.

La mala noticia es que no hay ganchos de filtro allí, que podríamos usar para modificar la ruta de destino para los archivos recién creados.

Krzysiek Dróżdż
fuente
Es una pena. Quería implementar el agradable Imager.js pero eso probablemente no funcionará sin eso.
Philipp Kühn
Parece que hace que las imágenes Imager.js invisible a los robots (Google, Facebook, etc), por lo tanto te aconsejo usarlo (a menos que también añadir manualmente una noscriptetiqueta)
fregante
Hmm no, no lo creo. Normalmente usa img-tags con un src de una imagen. Y agrega, además, una etiqueta de datos con una sintaxis de otras imágenes formatos: <img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />. Y luego el script comprueba qué tamaño tiene el img y carga el mejor tamaño de imagen para eso.
Philipp Kühn
@ PhilippKühn Yo también estoy decepcionado. Su idea era bastante clara y quería usarla para ordenar el directorio de subidas (eliminar las miniaturas no utilizadas después de que el cambio de tema sea doloroso en ...)
Krzysiek Dróżdż
@ KrzysiekDróżdż Hola, creo que lo tengo. Mira mi respuesta a continuación. Con esta solución, también puede ordenar a través de ftp las imágenes por nombre de archivo y eliminar fácilmente los tamaños de imagen no utilizados.
Philipp Kühn
1

Ok, creo que lo tengo! No es perfecto, pero está bien para esto, lo quería. Para mí, solo el ancho de una imagen es importante. La altura es inútil para mí. Especialmente para implementar Imager.js, la altura en la url de la imagen es inquietante.

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;
}

Con este código los nombres de archivo son como:

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

Es no es posible añadir una subcarpeta a los ficheros, puesto que si añado imágenes en un post / página siempre la fuente original será utilizado en su lugar. Y eliminar estas imágenes al eliminarlas tampoco funcionará. No estoy seguro de por qué.

Philipp Kühn
fuente