Cada tamanho de imagem personalizada no diretório de upload personalizado?

10

Desejo enviar meus tamanhos de imagem personalizados em pastas personalizadas. A pasta deve ter o nome da largura selecionada. Por exemplo:

Se eu adicionar esses tamanhos personalizados ...

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

Seria bom que as imagens enviadas fossem enviadas assim:

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

Isso é possível? Só descobri que posso alterar a pasta de envio global com o filtro upload_dir .

    
por Philipp Kühn 08.12.2013 / 22:29

4 respostas

20

Philipp, tudo é possível se você se concentrar nisso. Você pode resolver seu problema estendendo a classe do editor de imagem do WordPress.

Note que estou usando o WordPress 3.7 - Eu não verifiquei nenhum dos códigos abaixo nas versões anteriores e na última versão 3.8.


Noções básicas do Editor de Imagens

O WordPress tem duas classes internas que manipulam a manipulação de imagens:

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

Essas duas classes estendem WP_Image_Editor porque ambas usam um mecanismo de imagem diferente (GD e ImageMagick respectivamente) para carregar, redimensionar, compactar e salvar imagens.

Por padrão, o WordPress tentará usar o mecanismo ImageMagick primeiro, que precisa de uma extensão PHP, porque é geralmente preferido em relação ao mecanismo GD padrão do PHP. A maioria dos servidores compartilhados não tem a extensão ImageMagick ativada.


Adicionar um editor de imagens

Para decidir qual mecanismo usar, o WordPress chama uma função interna __wp_image_editor_choose() (localizada em /wp-includes/media.php ). Essa função percorre todos os mecanismos para ver qual mecanismo pode manipular a solicitação.

A função também possui um filtro chamado wp_image_editors , que permite adicionar mais editores de imagens, como:

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

    return $editors;
}

Observe que estamos prefixando nossa classe de editor de imagens personalizadas WP_Image_Editor_Custom , para que o WordPress possa verificar se o nosso mecanismo consegue lidar com o redimensionamento antes de testar outros mecanismos.


Criando nosso editor de imagens

Agora vamos escrever nosso próprio editor de imagens para que possamos decidir sobre nomes de arquivos para nós mesmos. O nome de arquivo é tratado pelo método WP_Image_Editor::generate_filename() (ambos os mecanismos herdam esse método), portanto, devemos sobrescrever isso em nossa classe personalizada.

Como planejamos apenas alterar nomes de arquivos, devemos estender um dos mecanismos existentes para que não seja necessário reinventar a roda. Estenderei WP_Image_Editor_GD no meu exemplo, pois você provavelmente não tem a extensão ImageMagick ativada. O código é intercambiável para uma configuração do ImageMagick. Você pode adicionar os dois se estiver planejando usar o tema em diferentes configurações.

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

A maior parte do código acima foi copiada diretamente da classe WP_Image_Editor e comentada para sua conveniência. A única mudança real é que o sufixo é agora um prefixo.

Como alternativa, você poderia apenas chamar parent::generate_filename() e usar um mb_str_replace() para alterar o sufixo em um prefixo, mas imaginei que estaria mais inclinado a errar.


Salvando novos caminhos para os metadados

Após fazer o upload de image.jpg , a pasta de uploads será exibida assim:

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

Até aí tudo bem. No entanto, ao chamar funções básicas como wp_get_attachment_image_src() , observaremos que todos os tamanhos de imagem são armazenados como image.jpg sem o novo caminho de diretório.

Podemos contornar esse problema salvando a nova estrutura de pastas nos metadados da imagem (onde os nomes dos arquivos são armazenados). Os dados são executados através de vários filtros ( wp_generate_attachment_metadata entre outros) antes de serem inseridos no banco de dados, mas como já estamos implementando um editor de imagens personalizado, podemos retornar à fonte dos metadados de tamanho de imagem: WP_Image_Editor::multi_resize() . Ele gera matrizes 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
    )
)

Substituiremos o método multi_resize() em nossa classe 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 você pode ver, não me incomodei em substituir qualquer código. Eu apenas chamo o método pai e deixo gerar os metadados. Em seguida, percorro o array resultante e ajustei o valor de file para cada tamanho.

Agora, wp_get_attachment_image_src($att_id, array(300, 300)) retorna 2013/12/300x300/image.jpg . Viva!


Considerações finais

Espero que isso forneça uma boa base para você elaborar. No entanto, tenha em atenção que se uma imagem for inferior ao tamanho especificado (por exemplo, 280 x 300), o sufixo gerado (prefixo no nosso caso) e os tamanhos das imagens são 280 x 300, não 300 x 300. Se você fizer upload de muitas imagens menores, você terá muitas pastas diferentes.

Uma boa solução seria usar o slug de tamanho como um nome de pasta ( small , medium , et cetera) ou expandir o código para tamanhos redondos até o tamanho de imagem preferido mais próximo.

Você observou que deseja usar apenas a largura como um nome de diretório. Esteja avisado - plugins ou temas podem gerar dois tamanhos diferentes com a mesma largura, mas diferentes alturas.

Além disso, você pode remover as pastas de ano / mês desativando "Organizar meus uploads em pastas com base no mês e no ano" em Configurações > Mídia ou manipulando generate_filename ainda mais.

Espero que isso ajude. Boa sorte!

    
por Robbert 13.12.2013 / 14:59
3

@ A resposta de Robbert foi um recurso divino em meus esforços para armazenar tamanhos alternativos gerados pelo WordPress em diretórios separados. Meu código também altera o diretório de upload para ./media, então certifique-se de editar essas linhas se você não quiser. Não é uma resposta exata à pergunta do primeiro post, mas oferece uma solução alternativa para o mesmo 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 sem problemas de acordo com os meus testes, embora eu não tenha tentado verificar como funciona com os populares plug-ins de galeria / mídia.

bônus relacionado: um utilitário bruto para excluir todas as miniaturas geradas pelo WordPress delete_deprecated_thumbs.php

    
por Arty2 06.03.2014 / 15:13
1

Eu olhei para essas partes do código do WordPress e tenho medo de não ter boas notícias.

Existem 2 classes:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick ,

ambos estendendo o resumo WP_Image_Editor class.

Essas classes estão implementando o método multi_resize , que é usado para gerar várias imagens de um upload.

A notícia realmente ruim é que não há ganchos de filtro, que poderíamos usar para modificar o caminho de destino para arquivos recém-criados.

    
por Krzysiek Dróżdż 12.12.2013 / 12:26
1

Ok, acho que entendi! Não perfeito, mas tudo bem para isso eu queria. Para mim apenas a largura de uma imagem é importante. Altura é inútil para mim. Especialmente para implementar o Imager.js , a altura no URL da imagem é preocupante.

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

Com este código, os nomes dos arquivos são 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

Não é possível adicionar uma subpasta aos nomes dos arquivos, porque se eu adicionar imagens em um post / página, sempre será usada a fonte original. E remover essas imagens na exclusão também não funcionará. Não sei porquê.

    
por Philipp Kühn 13.12.2013 / 13:21