Jede benutzerdefinierte Bildgröße im benutzerdefinierten Upload-Verzeichnis?

11

Ich möchte meine benutzerdefinierten Bildgrößen in benutzerdefinierten Ordnern hochladen. Der Ordner sollte den Namen der ausgewählten Breite haben. Beispielsweise:

Wenn ich diese benutzerdefinierten Größen hinzufüge ...

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

Es wäre schön, wenn die hochgeladenen Bilder so hochgeladen würden:

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

Ist das möglich? Ich habe nur festgestellt, dass ich den globalen Upload-Ordner mit dem Filter upload_dir ändern kann.

Philipp Kühn
quelle

Antworten:

21

Philipp, alles ist möglich, wenn du es dir überlegst. Sie können Ihr Problem lösen, indem Sie die WordPress-Bildeditorklasse erweitern.

Hinweis Ich verwende WordPress 3.7 - Ich habe in früheren Versionen und in der neuesten Version 3.8 keinen der folgenden Codes überprüft.


Grundlagen des Bildeditors

WordPress verfügt über zwei integrierte Klassen, die sich mit Bildmanipulationen befassen:

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

Diese beiden Klassen werden erweitert, WP_Image_Editorda beide eine unterschiedliche Bild-Engine (GD bzw. ImageMagick) zum Laden, Ändern der Größe, Komprimieren und Speichern von Bildern verwenden.

Standardmäßig versucht WordPress zuerst, die ImageMagick-Engine zu verwenden, für die eine PHP-Erweiterung erforderlich ist, da diese im Allgemeinen der Standard-GD-Engine von PHP vorgezogen wird. Bei den meisten gemeinsam genutzten Servern ist die ImageMagick-Erweiterung jedoch nicht aktiviert.


Fügen Sie einen Bildeditor hinzu

Um zu entscheiden, welche Engine verwendet werden soll, ruft WordPress eine interne Funktion auf __wp_image_editor_choose()(befindet sich in /wp-includes/media.php). Diese Funktion durchläuft alle Engines, um festzustellen, welche Engine die Anforderung verarbeiten kann.

Die Funktion verfügt auch über einen Filter wp_image_editors, mit dem Sie weitere Bildbearbeitungsprogramme hinzufügen können:

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

    return $editors;
}

Beachten Sie, dass wir unsere benutzerdefinierte Bildeditor- Klasse voranstellen,WP_Image_Editor_Custom damit WordPress prüft, ob unsere Engine die Größenänderung verarbeiten kann, bevor andere Engines getestet werden.


Erstellen unseres Bildeditors

Jetzt schreiben wir unseren eigenen Bildeditor, damit wir selbst über Dateinamen entscheiden können. Der Dateiname wird von der Methode behandeltWP_Image_Editor::generate_filename() (beide Engines erben diese Methode), daher sollten wir dies in unserer benutzerdefinierten Klasse überschreiben.

Da wir nur vorhaben, Dateinamen zu ändern, sollten wir einen der vorhandenen Motoren erweitern, damit wir das Rad nicht neu erfinden müssen. Ich werde WP_Image_Editor_GDin meinem Beispiel erweitern, da Sie wahrscheinlich die ImageMagick-Erweiterung nicht aktiviert haben. Der Code ist jedoch für ein ImageMagick-Setup austauschbar. Sie können beide hinzufügen, wenn Sie das Thema in verschiedenen Setups verwenden möchten.

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

Der größte Teil des obigen Codes wurde direkt von der kopiert WP_Image_Editor Klasse und zur Vereinfachung kommentiert. Die einzige tatsächliche Änderung besteht darin, dass das Suffix jetzt ein Präfix ist.

Alternativ können Sie einfach aufrufen parent::generate_filename()und ein verwenden mb_str_replace(), um das Suffix in ein Präfix zu ändern, aber ich dachte, das würde eher schief gehen.


Speichern neuer Pfade zu Metadaten

Nach dem Hochladen image.jpgsieht der Upload- Ordner folgendermaßen aus:

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

So weit, ist es gut. Beim Aufrufen von Grundfunktionen wie wp_get_attachment_image_src()werden wir jedoch feststellen, dass alle Bildgrößen wie image.jpgohne den neuen Verzeichnispfad gespeichert werden.

Wir können dieses Problem umgehen, indem wir die neue Ordnerstruktur in den Bildmetadaten speichern (in denen die Dateinamen gespeichert sind). Die Daten durchlaufen ( wp_generate_attachment_metadataunter anderem) verschiedene Filter, bevor sie in die Datenbank eingefügt werden. Da wir jedoch bereits einen benutzerdefinierten Bildeditor implementieren, können wir zur Quelle der Metadaten für die Bildgröße zurückkehren : WP_Image_Editor::multi_resize(). Es werden Arrays wie dieses generiert:

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

Wir werden die multi_resize()Methode in unserer benutzerdefinierten Klasse überschreiben :

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

Wie Sie sehen können, habe ich keinen Code ersetzt. Ich rufe einfach die übergeordnete Methode auf und lasse sie die Metadaten generieren. Dann durchlaufe ich das resultierende Array und passe den fileWert für jede Größe an.

Kehrt jetzt wp_get_attachment_image_src($att_id, array(300, 300))zurück 2013/12/300x300/image.jpg. Hurra!


Abschließende Gedanken

Ich hoffe, dies war eine gute Grundlage für Ihre Ausarbeitung. Beachten Sie jedoch, dass das generierte Suffix (in unserem Fall das Präfix) und die Bildgröße 280 x 300 und nicht 300 x 300 beträgt, wenn ein Bild kleiner als die angegebene Größe ist (z. B. 280 x 300). Wenn Sie viele kleinere Bilder hochladen, erhalten Sie viele verschiedene Ordner.

Eine gute Lösung wäre, entweder den Größen-Slug als Ordnernamen zu verwenden ( small,medium et cetera) oder den Code zu runde Größen erweitern , um die nächsten bevorzugte Bildgröße auf.

Sie haben festgestellt, dass Sie nur die Breite als Verzeichnisnamen verwenden möchten. Seien Sie jedoch gewarnt - Plugins oder Themes können zwei verschiedene Größen mit derselben Breite, aber unterschiedlichen Höhen erzeugen.

Sie können die Jahres- / Monatsordner auch entfernen, indem Sie entweder "Meine Uploads in Monats- und Jahresordnern organisieren" unter "Einstellungen"> "Medien" deaktivieren oder bearbeiten generate_filename noch weiter .

Hoffe das hilft. Viel Glück!

Robbert
quelle
3
Was für eine Antwort! : D Netter Mann!
Philipp Kühn
1
Bitte! Ich ging davon aus, dass Sie mindestens ein bisschen Erfahrung mit OOP- und WP-Filtern haben, aber wenn Sie noch etwas nicht verstehen, können Sie es gerne fragen. Danke für das Kopfgeld!
Robbert
2
@Robbert Ehrlich gesagt ist das genial. Ich habe mir wegen mangelnder Action und Filterhaken im Medien-Upload-System die Haare ausgerissen. Rückblickend scheint es offensichtlich, aber es war mir einfach nicht in den Sinn gekommen, die Bildbearbeitungsprogramme vollständig zu überschreiben. Dies löst so viele Probleme auf einen Schlag.
Jonathan Fingland
1
@ JonathanFingland Ha, ich gebe zu, ich musste für diesen einen weit ins Kaninchenloch gehen. Freut mich zu helfen!
Robbert
Kleine Anmerkung - im letzten Code-Ruhezustand (öffentliche Funktion multi_resize ($ Größen)) macht das Schlüsselwort "public" die Website herunter. Entfernen Sie es einfach und es ist wieder auf. 2k17 und deine Antwort ist immer noch großartig, danke !!
Paradoxetion
3

@ Robberts Antwort war eine göttliche Ressource in meinen Bemühungen, von WordPress generierte alternative Größen in separaten Verzeichnissen zu speichern. Mein Code ändert auch das Upload-Verzeichnis in ./media. Bearbeiten Sie diese Zeilen, wenn Sie dies nicht möchten. Es ist keine genaue Antwort auf die Frage des ersten Posters, bietet aber eine alternative Lösung für dasselbe Problem:

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

Funktioniert laut meinen Tests ohne Probleme, obwohl ich nicht versucht habe zu überprüfen, wie es mit beliebten Galerie- / Medien-Plugins abschneidet.

Zugehöriger Bonus: Ein Rohdienstprogramm zum Löschen aller von WordPress generierten Miniaturansichten delete_deprecated_thumbs.php

Arty2
quelle
1

Ich habe mir diese Teile des WordPress-Codes angesehen und fürchte, ich habe keine guten Nachrichten.

Es gibt 2 Klassen:

  • WP_Image_Editor_GD
  • WP_Image_Editor_Imagick,

beide erweitern abstrakte WP_Image_EditorKlasse.

Diese Klassen implementieren eine multi_resizeMethode, mit der mehrere Bilder aus einem hochgeladenen generiert werden.

Die wirklich schlechte Nachricht ist, dass es dort keine Filter-Hooks gibt, mit denen wir den Zielpfad für neu erstellte Dateien ändern könnten.

Krzysiek Dróżdż
quelle
Das ist eine Schande. Ich wollte die nette Imager.js implementieren, aber ohne das wird das wahrscheinlich nicht funktionieren.
Philipp Kühn
Es sieht so aus, als ob Imager.js Bilder für Bots (Google, Facebook usw.) unsichtbar macht. Daher würde ich davon abraten, sie zu verwenden (es sei denn, Sie fügen auch manuell ein noscriptTag hinzu)
fregante
Hmm nein, das glaube ich nicht. Normalerweise verwenden Sie img-Tags mit einem src eines Bildes. Und Sie fügen zusätzlich ein Daten-Tag mit einer Syntax anderer Bildgrößen hinzu : <img src="http://placehold.it/260" data-src="http://placehold.it/{width}" />. Und dann prüft das Skript, welche Größe das Bild hat und lädt die beste Bildgröße dafür.
Philipp Kühn
@ PhilippKühn Ich bin auch enttäuscht. Ihre Idee war ziemlich ordentlich und ich wollte sie verwenden, um das
Upload-Verzeichnis aufzuräumen
@ KrzysiekDróżdż Hey, ich glaube ich habe es verstanden. Schau dir meine Antwort unten an. Mit dieser Lösung können Sie die Bilder auch per FTP nach Dateinamen sortieren und die nicht verwendeten Bildgrößen einfach entfernen.
Philipp Kühn
1

Ok, ich glaube ich habe es verstanden! Nicht perfekt, aber okay dafür wollte ich es. Für mich ist nur die Breite eines Bildes wichtig. Höhe ist für mich nutzlos. Insbesondere für die Implementierung von Imager.js ist die Höhe in der Bild-URL störend.

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

Mit diesem Code lauten die Dateinamen wie folgt:

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 ist nicht möglich, den Dateinamen einen Unterordner hinzuzufügen, da beim Hinzufügen von Bildern zu einem Beitrag / einer Seite immer die Originalquelle verwendet wird. Das Entfernen dieser Bilder beim Löschen funktioniert ebenfalls nicht. Ich bin mir nicht sicher warum.

Philipp Kühn
quelle