Każdy niestandardowy rozmiar obrazu w niestandardowym katalogu przesyłania?

11

Chcę przesłać moje niestandardowe rozmiary obrazów do folderów niestandardowych. Folder powinien mieć nazwę wybranej szerokości. Na przykład:

Jeśli dodam te niestandardowe rozmiary ...

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

Byłoby miło, że przesłane zdjęcia są przesyłane w następujący sposób:

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

czy to możliwe? Odkryłem tylko, że mogę zmienić globalny folder przesyłania za pomocą filtra upload_dir .

Philipp Kühn
źródło

Odpowiedzi:

21

Philipp, wszystko jest możliwe, jeśli się na to zdecydujesz. Możesz rozwiązać ten problem, rozszerzając klasę edytora obrazów WordPress.

Uwaga Używam WordPress 3.7 - nie sprawdziłem żadnego z poniższych kodów we wcześniejszych wersjach i najnowszej wersji 3.8.


Podstawy edytora obrazów

WordPress ma dwie wbudowane klasy, które obsługują manipulowanie obrazami:

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

Te dwie klasy rozszerzają się, WP_Image_Editorponieważ obie wykorzystują inny silnik obrazu (odpowiednio GD i ImageMagick) do ładowania, zmiany rozmiaru, kompresji i zapisywania obrazów.

Domyślnie WordPress najpierw spróbuje użyć silnika ImageMagick, który wymaga rozszerzenia PHP, ponieważ jest ogólnie preferowany w stosunku do domyślnego silnika GD PHP. Większość udostępnianych serwerów nie ma jednak włączonego rozszerzenia ImageMagick.


Dodaj edytor zdjęć

Aby zdecydować, którego silnika użyć, WordPress wywołuje funkcję wewnętrzną __wp_image_editor_choose()(znajduje się w /wp-includes/media.php). Ta funkcja przechodzi przez wszystkie silniki, aby zobaczyć, który silnik może obsłużyć żądanie.

Ta funkcja ma także filtr o nazwie, wp_image_editorsktóry pozwala dodawać kolejne edytory obrazów:

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

    return $editors;
}

Uwaga: przygotowujemy naszą niestandardową klasę edytora obrazów, WP_Image_Editor_Customaby WordPress sprawdził, czy nasz silnik może obsłużyć zmianę rozmiaru przed przetestowaniem innych silników.


Tworzenie naszego edytora obrazów

Teraz napiszemy własny edytor obrazów, abyśmy mogli sami decydować o nazwach plików. Nazwy plików są obsługiwane przez tę metodę WP_Image_Editor::generate_filename()(oba silniki dziedziczą tę metodę), więc powinniśmy zastąpić to w naszej klasie niestandardowej.

Ponieważ planujemy tylko zmieniać nazwy plików, powinniśmy rozszerzyć jeden z istniejących silników, abyśmy nie musieli wymyślać koła na nowo. Rozszerzę WP_Image_Editor_GDw moim przykładzie, ponieważ prawdopodobnie nie masz włączonego rozszerzenia ImageMagick. Kod jest jednak wymienny dla konfiguracji ImageMagick. Możesz dodać oba, jeśli planujesz używać motywu w różnych konfiguracjach.

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

Większość powyższego kodu została bezpośrednio skopiowana z WP_Image_Editorklasy i skomentowana dla Twojej wygody. Jedyną faktyczną zmianą jest to, że przyrostek jest teraz przedrostkiem.

Alternatywnie, możesz po prostu zadzwonić parent::generate_filename()i użyć an, mb_str_replace()aby zmienić sufiks na prefiks, ale doszedłem do wniosku, że bardziej skłonny byłby się pomylić.


Zapisywanie nowych ścieżek do metadanych

Po image.jpgprzesłaniu folder przesyłania wygląda następująco:

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

Na razie w porządku. Jednak podczas wywoływania podstawowych funkcji, takich jak wp_get_attachment_image_src(), zauważymy, że wszystkie rozmiary obrazów są przechowywane tak, jak image.jpgbez nowej ścieżki do katalogu.

Możemy obejść ten problem, zapisując nową strukturę folderów w metadanych obrazu (gdzie przechowywane są nazwy plików). Trasy danych przez różnych filtrów ( wp_generate_attachment_metadatamiędzy innymi) zanim zostanie wprowadzony do bazy danych, ale skoro jesteśmy już wdrożenie niestandardowych edytora obrazu, możemy podróżować z powrotem do źródła metadanych Wielkość obrazu: WP_Image_Editor::multi_resize(). Generuje tablice takie jak ta:

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

Zastąpimy multi_resize()metodę w naszej klasie niestandardowej:

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

Jak widać, nie zawracałem sobie głowy wymianą żadnego kodu. Po prostu wywołuję metodę nadrzędną i pozwalam jej generować metadane. Następnie przeglądam wynikową tablicę i dostosowuję filewartość dla każdego rozmiaru.

Teraz wp_get_attachment_image_src($att_id, array(300, 300))wraca 2013/12/300x300/image.jpg. Brawo!


Końcowe przemyślenia

Mam nadzieję, że stanowiło to dobrą podstawę do opracowania. Pamiętaj jednak, że jeśli obraz jest mniejszy niż określony rozmiar (np. 280 x 300), wygenerowany sufiks (w naszym przypadku przedrostek) i rozmiary obrazów to 280 x 300, a nie 300 x 300. Jeśli prześlesz dużo mniejszych obrazów, otrzymasz wiele różnych folderów.

Dobrym rozwiązaniem byłoby albo użyć informacji o rozmiarze jako nazwy folderu ( small, mediumitd.) Lub rozwinąć kod do okrągłych rozmiarów do najbliższego preferowanego rozmiaru obrazu.

Zauważyłeś, że chcesz użyć tylko szerokości jako nazwy katalogu. Ostrzegamy - wtyczki lub motywy mogą generować dwa różne rozmiary o tej samej szerokości, ale o różnych wysokościach.

Ponadto możesz usunąć foldery roku / miesiąca, wyłączając opcję „Organizuj moje przesłane pliki w folderach na miesiąc i rok” w Ustawieniach> Media lub manipulując generate_filenamejeszcze bardziej.

Mam nadzieję że to pomoże. Powodzenia!

Robbert
źródło
3
Co za odpowiedź! : D Miły człowiek!
Philipp Kühn,
1
Nie ma za co! Zakładałem, że masz przynajmniej trochę doświadczenia z filtrami OOP i WP, ale jeśli jest coś, czego jeszcze nie rozumiesz, możesz zapytać. Dziękuję za nagrodę!
Robbert,
2
@Robbert Szczerze mówiąc, to jest genialne. Oderwałem włosy z powodu braku akcji i haków filtrów w systemie przesyłania mediów. Z perspektywy czasu wydaje się to oczywiste, ale po prostu nie przyszło mi do głowy, aby całkowicie zastąpić edytory obrazów. Takie postępowanie rozwiązuje tak wiele problemów za jednym zamachem.
Jonathan Fingland
1
@JonathanFingland Ha, przyznaję, że musiałem pójść daleko w dół króliczej nory. Cieszę się, że mogę pomóc!
Robbert
Mała uwaga - w ostatnim spokoju kodu (funkcja publiczna multi_resize (rozmiary $)) słowo kluczowe „public” powoduje wyłączenie witryny. Po prostu go usuń i znów będzie gotowe. 2k17, a twoja odpowiedź jest nadal świetna, dziękuję !!
Paradoxetion
3

Odpowiedź Robberta była boskim zasobem w moich wysiłkach przechowywania alternatywnych rozmiarów generowanych przez WordPress w osobnych katalogach. Mój kod zmienia również katalog przesyłania na ./media, więc edytuj te linie, jeśli nie chcesz. To nie jest dokładna odpowiedź na pytanie pierwszego plakatu, ale oferuje alternatywne rozwiązanie tego samego problemu:

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

Zgodnie z moimi testami działa bez żadnych problemów, chociaż nie próbowałem sprawdzić, jak sobie radzi z popularnymi wtyczkami galerii / multimediów.

powiązany bonus: surowe narzędzie do usuwania wszystkich wygenerowanych przez WordPress miniatur delete_deprecated_thumbs.php

Arty2
źródło
1

Przejrzałem te części kodu WordPress i obawiam się, że nie mam dobrych wiadomości.

Istnieją 2 klasy:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick,

oba rozszerzające WP_Image_Editorklasę abstrakcyjną .

Klasy te implementują multi_resizemetodę, która służy do generowania wielu obrazów z przesłanego jednego.

Naprawdę złą wiadomością jest to, że nie ma tam żadnych zaczepów filtrów, których moglibyśmy użyć do zmodyfikowania ścieżki docelowej dla nowo tworzonych plików.

Krzysiek Dróżdż
źródło
Jaka szkoda. Chciałem zaimplementować ładne Imager.js, ale bez tego prawdopodobnie nie zadziała.
Philipp Kühn,
Wygląda na to, że Imager.js sprawia, że ​​obrazy są niewidoczne dla botów (Google, Facebook itp.), Dlatego odradzam korzystanie z nich (chyba że dodasz również noscripttag ręcznie )
fregante
Hmm nie, nie sądzę. Zwykle używasz tagów img z src obrazu. I dodać dodatkowo dane tag o składni innych obrazów wielkościach: <img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />. Następnie skrypt sprawdza, jaki rozmiar ma img i ładuje najlepszy rozmiar obrazu do tego.
Philipp Kühn
@ PhilippKühn Też jestem rozczarowany. Twój pomysł był całkiem schludny i chciałem go wykorzystać do uporządkowania katalogu wysyłania (usuwanie nieużywanych miniatur po zmianie motywu jest bólem w ...)
Krzysiek Dróżdż
@ KrzysiekDróżdż Hej, myślę, że mam. Spójrz na moją odpowiedź poniżej. Dzięki temu rozwiązaniu możesz również sortować obrazy za pomocą ftp według nazwy pliku i łatwo usuwać nieużywane rozmiary obrazów.
Philipp Kühn
1

Ok, myślę, że mam! Nie idealnie, ale w porządku, bo tego chciałem. Dla mnie ważna jest tylko szerokość obrazu. Wzrost jest dla mnie bezużyteczny. Szczególnie w przypadku implementacji Imager.js wysokość adresu URL obrazu jest niepokojąca.

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

W tym kodzie nazwy plików są następujące:

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

To nie możliwe, aby dodać podfolder do nazwach plików, ponieważ jeśli dodam obrazy w post / strona zawsze pierwotnym źródłem będzie używany. Usunięcie tych obrazów podczas usuwania również nie będzie działać. Nie jestem pewien dlaczego.

Philipp Kühn
źródło