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

11

Quero carregar 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 carregadas sejam carregadas 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? Descobri apenas que posso alterar a pasta de upload global com o filtro upload_dir .

Philipp Kühn
fonte

Respostas:

21

Philipp, tudo é possível se você se dedicar a isso. Você pode resolver seu problema estendendo a classe do editor de imagens do WordPress.

Nota: estou usando o WordPress 3.7 - não verifiquei nenhum dos códigos abaixo nas versões anteriores e na versão 3.8 mais recente.


Noções básicas do Image Editor

O WordPress possui duas classes integradas que lidam com 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 se estendem WP_Image_Editorporque 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 é preferível 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 lidar com a solicitação.

A função também possui um filtro chamado wp_image_editorsque permite adicionar mais editores de imagem, 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 anexando nossa classe personalizada de editor de imagens WP_Image_Editor_Custompara que o WordPress verifique se nosso mecanismo pode 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 os nomes dos arquivos. A nomeação de arquivos é tratada pelo método WP_Image_Editor::generate_filename()(ambos os mecanismos herdam esse método), portanto, devemos substituí-lo em nossa classe personalizada.

Como planejamos apenas alterar os nomes de arquivos, devemos estender um dos mecanismos existentes para não precisar reinventar a roda. Vou estender WP_Image_Editor_GDmeu 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 ambos 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 WP_Image_Editorclasse e comentada para sua conveniência. A única mudança real é que o sufixo agora é um prefixo.

Como alternativa, você pode simplesmente chamar parent::generate_filename()e usar um mb_str_replace()para mudar o sufixo para um prefixo, mas achei que seria mais provável que desse errado.


Salvando Novos Caminhos para Metadados

Após o upload image.jpg, a pasta de uploads fica assim:

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

Por enquanto, tudo bem. No entanto, ao chamar funções básicas como wp_get_attachment_image_src(), notamos que todos os tamanhos de imagem são armazenados como image.jpgsem o novo caminho do diretório.

Podemos solucionar esse problema salvando a nova estrutura de pastas nos metadados da imagem (onde os nomes dos arquivos estão armazenados). Os dados executado através de vários filtros ( wp_generate_attachment_metadataentre outros), antes de ser inserido no banco de dados, mas uma vez que já está implementando um editor de imagem personalizada, podemos viajar de volta para a fonte de tamanho da imagem metadados: 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
    )
)

Sobrescreveremos o multi_resize()método 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, eu não me incomodei em substituir nenhum código. Eu apenas chamo o método pai e deixo gerar os metadados. Em seguida, percorro a matriz resultante e ajusto o filevalor para cada tamanho.

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


Pensamentos finais

Espero que isso tenha fornecido uma boa base para você elaborar. No entanto, observe se uma imagem é menor que o tamanho especificado (por exemplo, 280x300), o sufixo gerado (prefixo no nosso caso) e os tamanhos de imagem são 280x300, não 300x300. Se você enviar muitas imagens menores, terá muitas pastas diferentes.

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

Você notou 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 com alturas diferentes.

Além disso, você pode remover as pastas de ano / mês desabilitando 'Organizar meus envios em pastas baseadas em mês e ano' em Configurações> Mídia ou manipulando generate_filenameainda mais.

Espero que isto ajude. Boa sorte!

Robbert
fonte
3
Que resposta! : D Homem legal!
Philipp Kühn
1
De nada! Eu estava assumindo que você tem pelo menos um pouco de experiência em filtros OOP e WP, mas se houver algo que você ainda não entende, sinta-se à vontade para perguntar. Obrigado pela recompensa!
Robbert
2
@ Robert Francamente, isso é brilhante. Eu estava arrancando os cabelos com a falta de ação e os ganchos de filtro no sistema de upload de mídia. Parece óbvio em retrospecto, mas simplesmente não me ocorreu substituir completamente os editores de imagem. Fazer isso resolve muitos problemas de uma só vez.
27616 Jonathan Fingland
1
@ JonathanFingland Ha, eu admito que tive que ir muito longe na toca do coelho para esta. Feliz por ajudar!
Robbert
Nota pequena - na última paz de código (função pública multi_resize ($ tamanhos)) a palavra-chave "public" reduz o site. Basta removê-lo e voltará a funcionar. 2k17 e sua resposta ainda é incrível, obrigado !!
Paradoxeção 25/01
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, portanto, certifique-se de editar essas linhas, se você não quiser. Não é uma resposta exata para a pergunta do primeiro pôster, 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 meus testes, embora eu não tenha tentado verificar como ele se sai com os populares plugins de galeria / mídia.

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

Arty2
fonte
1

Examinei 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 a WP_Image_Editorclasse abstrata .

Essas classes estão implementando o multi_resizemétodo, usado para gerar várias imagens a partir de uma carregada.

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.

Krzysiek Dróżdż
fonte
Isso é uma vergonha. Eu queria implementar o bom Imager.js, mas isso provavelmente não funcionará sem isso.
Philipp Kühn
Parece que Imager.js torna as imagens invisíveis aos bots (Google, Facebook, etc), por isso eu aconselho a usá-lo (a menos que você também adicionar manualmente uma noscripttag)
fregante
Hmm não, acho que não. Você normalmente usa tags img com um src de uma imagem. E você adicionar, adicionalmente, um tag de dados com uma sintaxe de outras imagens de tamanhos: <img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />. E então o script verifica qual o tamanho do img e carrega o melhor tamanho de imagem para isso.
Philipp Kühn
@ PhilippKühn Também estou decepcionado. Sua idéia era muito arrumado e eu queria usá-lo para arrumar envios de diretório (excluindo as miniaturas não utilizados após alteração tema é dor na ...)
Krzysiek Drozdz
@ KrzysiekDróżdż Ei, acho que entendi. Veja minha resposta abaixo. Com esta solução, você também pode classificar via ftp as imagens pelo nome do arquivo e remover facilmente os tamanhos de imagem não utilizados.
Philipp Kühn
1

Ok, acho que entendi! Não é perfeito, mas tudo bem para isso que 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 é perturbadora.

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 esse código, os nomes de 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 para os nomes dos arquivos, porque se eu adicionar imagens em um post / página sempre a fonte original será usado. E remover essas imagens ao excluir também não funcionará. Não sei por que.

Philipp Kühn
fonte