Das Hochladen von Bildern in die Medienbibliothek schlägt mit Speicher erschöpft fehl

Ich versuche, täglich viele hundert Bilder von einem Ordner auf dem Server in die Medienbibliothek hochzuladen, indem ich das folgende über CRON geplante Skript verwende:

 $wp_upload_dir['url'] . '/' . basename( $filename ), 'post_mime_type' => $fileType['type'], 'post_parent' => $postId, 'post_title' => preg_replace('/\.[^.]+$/', '', $filename), 'post_content' => '', 'post_status' => 'inherit' ]; $attachmentId = wp_insert_attachment($attachment, $uploadFile['file'], $postId); if (! is_wp_error($attachmentId)) { $attachmentData = wp_generate_attachment_metadata($attachmentId, $uploadFile['file']); wp_update_attachment_metadata($attachmentId, $attachmentData); } } else { echo 'Error: ' . $uploadFile['error'] . ''; } if ($attachmentId > 0) { $succeededFileCount++; echo 'File import succeeded: ' . $filePath . "
"; if (! unlink($filePath)) { echo 'Unable to delete file ' . $filePath . " after import.
"; } $page = get_post($postId); if ($page->post_content) { $content = $page->post_content; $start = strpos($content, ""); $shortcode = substr($content, $start, $end); $attrs = shortcode_parse_atts($shortcode); $attrs["ids"] .= "," . $attachmentId; $tempIds = explode(",", $attrs["ids"]); $tempIds = array_filter($tempIds); rsort($tempIds); $attrs["ids"] = implode(",", $tempIds); $shortcode = ""; foreach ($attrs as $key => $value) { if (strlen($shortcode) > 0) { $shortcode .= " "; } $shortcode .= $key . "=\"" . $value . "\""; } $newContent = substr($content, 0, $start); $newContent .= $shortcode; $newContent .= substr($content, $start + $end, strlen($content)); $page->post_content = $newContent; wp_update_post($page); } } } } echo $succeededFileCount . " files uploaded and imported successfully.
"; echo $failedFileCount . " files failed to uploaded or import successfully."; } get_header(); if (get_option('rmm_image_importer_key') != urldecode($_GET['key'])) { echo '
'; echo "

Incorrect authentication key: you are not allowed to import images into this site.

"; } else { echo '

'; $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $starttime = $mtime; $dataset = get_option('rmm_image_importer_settings'); if (is_array($dataset)) { foreach ($dataset as $data) { if (isset($data['folder']) || isset($data['page'])) { ?>

Import from folder:

<?php } } } $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $endtime = $mtime; $totaltime = ($endtime - $starttime); echo 'Files imported to media library over ' . $totaltime . ' seconds.

'; } get_footer();

Das Problem ist, dass das Skript, egal was ich mache, nach nur zwei Bildern mit diesem Fehler fehlschlägt:

Schwerwiegender Fehler: Zulässige Speichergröße von 1073741824 Bytes erschöpft (versucht, 28672 Byte zuzuweisen) in /home/forge/morselandcompany.com/public/wordpress/wp-includes/wp-db.php in Zeile 1841

Ich habe das Speicherlimit in WordPress auf 1024M eingestellt, sowie PHP. Ich verstehe nicht, warum dieses Skript wirklich mehr als 128M benötigt. Wie kann dieses Skript optimiert werden, um ordnungsgemäß zu funktionieren? (Die durchschnittliche Bildgröße beträgt 800 KB.)

Ein erstes Debugging mit BlackFire.io schlägt folgende Speichererrors vor: – wpdb-> query: 3.2MB, 31 mal aufgerufen – mysqli_fetch_object: 1.89MB, 595 mal aufgerufen – run_init () in wp-settings.php: 5.4MB, einmal aufgerufen Insgesamt schlägt Blackfire vor, dass mehr als 8 MB benötigt werden, um dieses Skript auszuführen!

Ich habe auch getestet mit allen Plugins deaktiviert, und das endete mit dem gleichen Ergebnis.

Ich laufe weiter – PHP 7.1
– Ubuntu 16.04
– DigitalOcean VPS (1 CPU, 1 GB RAM)
– WordPress 4.8
– NGINX 1.11.5

Danke für jede Hilfe!

Update: Im Interesse der Vollständigkeit, damit andere die mit get_post und wp_update_post verbundene Lösung für Speicherlecks nutzen können, habe ich den finalisierten Code gepostet, der das obige Problem getriggers hat. Wie Sie sehen, bestand die Lösung darin, meine eigenen Abfragen mit $ wpdb zu schreiben, anstatt sich auf die beiden WP-Methoden zu verlassen, die Speicherlecks verursachen:

  0) { $succeededFileCount++; echo 'File import succeeded: ' . $filePath . "
"; if (! unlink($filePath)) { echo 'Unable to delete file ' . $filePath . " after import.
"; } global $wpdb; $page = $wpdb->get_results("SELECT * FROM wp_posts WHERE ID = {$postId}")[0]; if (is_array($page)) { $page = $page[0]; } if ($page->post_content) { $content = $page->post_content; $start = strpos($content, ""); $shortcode = substr($content, $start, $end); $attrs = shortcode_parse_atts($shortcode); $attrs["ids"] .= "," . $attachmentId; $tempIds = explode(",", $attrs["ids"]); $tempIds = array_filter($tempIds); rsort($tempIds); $attrs["ids"] = implode(",", $tempIds); $shortcode = ""; foreach ($attrs as $key => $value) { if (strlen($shortcode) > 0) { $shortcode .= " "; } $shortcode .= $key . "=\"" . $value . "\""; } $newContent = substr($content, 0, $start); $newContent .= $shortcode; $newContent .= substr($content, $start + $end, strlen($content)); $wpdb->update( 'post_content', ['post_content' => $newContent], ['ID' => $postId] ); } } } } echo $succeededFileCount . " files uploaded and imported successfully.
"; echo $failedFileCount . " files failed to uploaded or import successfully."; } get_header(); if (get_option('rmm_image_importer_key') != urldecode($_GET['key'])) { echo '
'; echo "

Incorrect authentication key: you are not allowed to import images into this site.

"; } else { echo '

'; $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $starttime = $mtime; $dataset = get_option('rmm_image_importer_settings'); if (is_array($dataset)) { foreach ($dataset as $data) { if (isset($data['folder']) || isset($data['page'])) { ?>

Import from folder:

<?php } } } $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $endtime = $mtime; $totaltime = ($endtime - $starttime); echo 'Files imported to media library over ' . $totaltime . ' seconds.

'; } get_footer();

Solutions Collecting From Web of "Das Hochladen von Bildern in die Medienbibliothek schlägt mit Speicher erschöpft fehl"

Ein paar Dinge

  • Verwende media_handle_sideload damit WordPress die Dateien an den richtigen Ort verschiebt und sie für dich validiert, den Anhang erstellt, etc, keine dieser manuellen Sachen
  • Führe das nicht einmal durch und erwarte, dass es alles tut. Sie werden nur auf das gleiche Problem stoßen, aber weiter in den Import. Wenn Sie ihm unendlichen Speicher geben, haben Sie ein Zeitlimitausführungsproblem, bei dem das Skript einfach keine Zeit mehr hat
  • Verarbeiten Sie jeweils 5 Dateien und rufen Sie sie wiederholt auf, bis nichts mehr verarbeitet werden kann
  • Verwenden Sie einen WP CLI-Befehl, booten Sie WordPress nicht und rufen Sie es über die GUI auf. Rufen Sie es direkt von Cron an und überspringen Sie das Pingen eines URL-Geschäfts. CLI-Befehle erhalten für ihre Arbeit unbegrenzte Zeit, und Sie können sie nicht über den Browser aufrufen. Die GET-Variable mit dem Schlüssel wird komplett überflüssig.
  • Escape Escape Escape, Sie echoten diese Arrays und Werte aus, vorausgesetzt, dass das, was sie enthalten, sicher ist, aber was passiert, wenn ich ein Skript-Tag dort hineinschleppe? Unable to delete file after import. . Größter Sicherheitsschritt, den du machen kannst, der den größten Unterschied macht, aber am wenigsten benutzt

Ein Prototyp WP CLI-Befehl

Hier ist ein einfacher WP CLI-Befehl, der den Trick machen sollte. Ich habe es nicht getestet, aber alle wichtigen Teile sind da, ich vertraue darauf, dass du kein absoluter Neuling bist, wenn es um PHP geht und keine lockeren Schrauben oder kleine Fehler anziehen kann, keine zusätzlichen API-Kenntnisse notwendig.

Sie möchten es nur in einem WP-CLI-Kontext einschließen, beispielsweise:

 if ( defined( 'WP_CLI' ) && WP_CLI ) { require_once dirname( __FILE__ ) . '/inc/class-plugin-cli-command.php'; } 

Ändern Sie entsprechend, indem Sie den Befehl in functions.php eines Themes ablegen und erwarten, dass alles funktioniert, werden Fehler verursacht, da WP CLI-classn nur in der Befehlszeile geladen werden, niemals bei der Bearbeitung einer Browser-Anfrage.

Verwendung:

wp mbimport run

class:

 < ?php /** * Implements image importer command. */ class MBronner_Import_Images extends WP_CLI_Command { /** * Runs the import script and imports several images * * ## EXAMPLES * * wp mbimport run * * @when after_wp_load */ function run( $args, $assoc_args ) { if ( !function_exists('media_handle_upload') ) { require_once(ABSPATH . "wp-admin" . '/includes/image.php'); require_once(ABSPATH . "wp-admin" . '/includes/file.php'); require_once(ABSPATH . "wp-admin" . '/includes/media.php'); } // Set the directory $dir = ABSPATH .'/wpse'; // Define the file type $images = glob( $dir . "*.jpg" ); if ( empty( $images ) { WP_CLI::success( 'no images to import' ); exit; } // Run a loop and transfer every file to media library // $count = 0; foreach ( $images as $image ) { $file_array = array(); $file_array['name'] = $image; $file_array['tmp_name'] = $image; $id = media_handle_sideload( $file_array, 0 ); if ( is_wp_error( $id ) ) { WP_CLI::error( "failed to sideload ".$image ); exit; } // only do 5 at a time, dont worry we can run this // several times till they're all done $count++; if ( $count === 5 ) { break; } } WP_CLI::success( "import ran" ); } } WP_CLI::add_command( 'mbimport', 'MBronner_Import_Images' ); 

Rufen Sie wiederholt von einem echten Cron-Job an. Wenn dies nicht möglich ist, verwenden Sie entweder WP Cron oder einen Haken bei admin_init , der nach einer GET-Variablen admin_init . Verwenden Sie den Code innerhalb des Befehls run mit einigen Änderungen.

Wenn WP CLI keine Option ist

Die Verwendung einer eigenständigen PHP-Datei, die WP bootstrappt, ist ein Sicherheitsrisiko und ein großes Angriffsziel für Angreifer, wenn sie Ihre Serverressourcen auslasten möchten (oder doppelte Probleme durch mehrfaches gleichzeitiges Treffen der URL auslösen).

Beispielsweise:

 // example.com/?mbimport=true add_action( 'init', function() { if ( $_GET['action'] !== 'mbimport' ) { return; } if ( $_GET['key'] !== get_option('key thing' ) ) { return; } // the code from the run function in the CLI command, but with the WP_CLI::success bits swapped out // ... exit; } 

Wiederholtes Anrufen

Es kann sein, dass Ihr externer Dienst dies nicht wiederholt aufrufen kann. Zu dem ich sage:

  • Verlassen Sie sich nicht auf den externen Dienst, sondern rufen Sie ihn auf Ihrem eigenen Server an, auch wenn Sie keine Arbeit erledigen müssen
  • Eine standardmäßige WP Cron-Aufgabe würde ebenfalls funktionieren
  • Führe es alle 5 Minuten aus
  • Veranlassen Sie, dass sich die Aufgabe selbst aufruft, wenn noch etwas zu tun ist, und verwenden Sie eine nicht blockierende Anfrage. Auf diese Weise wird es neue Instanzen erzeugen, bis es fertig ist, zB

      if ( $count === 5 ) { wp_remote_get( home_url('?mbimport=true&key=abc'), [ 'blocking' => false ]); exit; ) 

GUI?

Wenn Sie eine Fortschrittsanzeige für eine Benutzeroberfläche im Dashboard wünschen, zählen Sie einfach, wie viele JPEG-Dateien in dem zu importierenden Ordner verbleiben. Wenn Sie es konfigurieren müssen, erstellen Sie eine Benutzeroberfläche und speichern Sie die Einstellungen in den Optionen und ziehen Sie dann aus den Optionen im CLI-Skript.

Haben Sie über die Verwendung der REST-API nachgedacht?

Verschieben Sie den gesamten process und fügen Sie die Dateien über die REST-API hinzu. Sie können eine POST-Anfrage an example.com/wp-json/wp/v2/media , um die JPEGs hochzuladen. Kein Code auf Ihrer Website erforderlich

https://stackoverflow.com/questions/37432114/wp-rest-api-upload-image

Es gibt bereits eine eingebaute function, die nur für diesen Zweck erstellt wurde. Sie müssen keine Codewände schreiben, um Bilder von Ihrer Festplatte hochzuladen. Sie können stattdessen media_sideload_image verwenden.

Diese function lädt Ihre Dateien hoch, kümmert sich um den Dateinamen, das Datum, die ID und den Rest des Materials.

Ich habe dies nicht mit absolutem Pfad getestet (Es benötigt die URL), aber es wäre einfach, den absoluten Pfad in URLs umzuwandeln, basierend auf Ihrer Fähigkeit, das obige Skript zu schreiben.

 // Set the directory $dir = ABSPATH .'/wpse'; // Define the file type $images = glob($directory . "*.jpg"); // Run a loop and upload every file to media library foreach($images as $image) { // Upload a single image media_sideload_image($image,'SOME POST ID HERE'); } 

Das ist alles was du brauchst. Bilder müssen an einen Post angehängt werden, aber Sie können sie später wieder lösen.

Ihr Server kann auch die gesamte Upload-Kapazität begrenzen, nicht nur was Sie im Code festgelegt haben. Erkundige dich bei deinem Provider oder wenn du Zugang zum WHM hast, ändere den maxium php upload für den Account (normalerweise 8GB) und du musst die php.ini ändern. Die folgende URL war hilfreich für mich, als ich dasselbe tun musste:

http://www.wpbeginner.com/wp-tutorials/how-to-increase-the-maximum-file-upload-size-in-wordpress/

Es ist jedoch keine vollständige Lösung, wenn der Server, auf dem Sie sich befinden, ein Limit hat.