Intereting Posts
Все сообщения отображаются по умолчанию index.html Как запросить персонализированный тип почтового сообщения? Что такое переменная этого экземпляра в классе Widgets Как читать сгенерированные значения из функции, расположенной в заголовке, полученной в ответ Ajax Как отображать подкатегории на странице продуктов с использованием woocommerce с мистической темой Почему при загрузке медиафайла второй раз восстанавливается старая версия? Переписать категорию slug Класс контейнера, не отображающийся в меню навигации Thumbnail Посмотрите на WP Список категорий подряд Перевести навигационное меню и названия виджета боковой панели Удалить все атрибуты заголовка ссылки фильтровать сообщения мета-ключом с разбивкой на страницы В том числе таксономический термин до типа post разбивает страницы верхнего уровня Как перенаправление блоков на одну страницу сообщения, если совпадение соответствует?

Загрузка изображений в медиабиблиотеку не выполняется с исчерпанной памятью

Я пытаюсь загрузить много сотен изображений ежедневно из папки на сервере в медиа-библиотеку, используя следующий скрипт, который запланирован через CRON:

<?php require_once('../../../../public/wordpress/wp-load.php'); require_once('../../../../public/wordpress/wp-admin/includes/image.php'); function importImage($imagePath, $postId) { $succeededFileCount = 0; $failedFileCount = 0; $files = scandir($imagePath); foreach ($files as $file) { if (in_array($file, ['.', '..'])) { continue; } $newPath = $imagePath . "/" . $file; $filePath = realpath($newPath); if (is_dir($newPath) && $item != '.' && $item != '..' && $item != 'failed_files') { importImage($newPath, $postId); } elseif ($item != '.' && $item != '..' && $item != 'failed_files') { $filename = basename($file); $uploadFile = wp_upload_bits($filename, null, file_get_contents($filePath)); $wp_upload_dir = wp_upload_dir(); if (! $uploadFile['error']) { $fileType = wp_check_filetype($filename, null); $attachment = [ 'guid' => $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 '<span style="color: red; font-weight: bold;">Error: ' . $uploadFile['error'] . '</span>'; } if ($attachmentId > 0) { $succeededFileCount++; echo '<span style="color: green; font-weight: normal;">File import succeeded: ' . $filePath . "</span><br />"; if (! unlink($filePath)) { echo '<span style="color: red; font-weight: bold;">Unable to delete file ' . $filePath . " after import.</span><br />"; } $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. <br />"; echo $failedFileCount . " files failed to uploaded or import successfully."; } get_header(); if (get_option('rmm_image_importer_key') != urldecode($_GET['key'])) { echo '<div id="message" class="error">'; echo "<p><strong>Incorrect authentication key: you are not allowed to import images into this site.</strong></p></div>"; } else { echo '<br /><br />'; $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'])) { ?> <h2>Import from folder: <?php echo $data['folder']; ?></h2> <p> <?php importImage(realpath(str_replace('//', '/', ABSPATH . '../../' . $data['folder'])), $data['page']); ?> </p> <?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.<br /><br />'; } get_footer(); 

Проблема в том, что независимо от того, что я делаю, скрипт терпит неудачу после двух изображений с этой ошибкой:

Неустранимая ошибка: допустимый объем памяти 1073741824 байт исчерпан (пытался выделить 28672 байта) в /home/forge/morselandcompany.com/public/wordpress/wp-includes/wp-db.php в строке 1841

У меня есть ограничение памяти, равное 1024 М в wordpress, а также PHP. Я не понимаю, почему этот скрипт должен нуждаться в более чем 128M, действительно. Как можно оптимизировать этот сценарий для правильной работы? (Средний размер изображения – 800 КБ).

Некоторая начальная отладка с BlackFire.io предлагает следующие типы памяти: – wpdb-> query: 3.2MB, называемый 31 раз – mysqli_fetch_object: 1.89MB, называемый 595 раз – run_init () в wp-settings.php: 5.4MB, вызываемый один раз В общем, blackfire предполагает, что для запуска этого скрипта требуется более 8 МБ!

Я также тестировал все отключенные плагины, и это закончилось тем же результатом.

Я запускаю – PHP 7.1
– Ubuntu 16.04
– DigitalOcean VPS (1 процессор, 1 ГБ оперативной памяти)
– WordPress 4.8
– NGINX 1.11.5

Спасибо за любую помощь!

Обновление: в интересах полноты, чтобы другие могли использовать решение для утечек памяти, связанных с get_post и wp_update_post, я опубликовал окончательный код, который решил проблему выше. Как вы можете видеть, решение заключалось в том, чтобы написать собственные запросы с помощью $ wpdb вместо того, чтобы полагаться на два метода WP, вызывающих утечки памяти:

 <?php require_once('../../../../public/wordpress/wp-load.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/image.php'); function importImage($imagePath, $postId) { $succeededFileCount = 0; $failedFileCount = 0; $files = scandir($imagePath); foreach ($files as $file) { if (in_array($file, ['.', '..'])) { continue; } $newPath = $imagePath . "/" . $file; $filePath = realpath($newPath); if (is_dir($newPath) && $file != 'failed_files') { importImage($newPath, $postId); } elseif ($file != 'failed_files') { $webPath = str_replace($_SERVER['DOCUMENT_ROOT'], '', $imagePath); $imageUrl = str_replace('/wordpress', '', get_site_url(null, "{$webPath}/" . urlencode($file))); $imageUrl = str_replace(':8000', '', $imageUrl); $attachmentId = media_sideload_image($imageUrl, 0, '', 'id'); if ($attachmentId > 0) { $succeededFileCount++; echo '<span style="color: green; font-weight: normal;">File import succeeded: ' . $filePath . "</span><br />"; if (! unlink($filePath)) { echo '<span style="color: red; font-weight: bold;">Unable to delete file ' . $filePath . " after import.</span><br />"; } 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. <br />"; echo $failedFileCount . " files failed to uploaded or import successfully."; } get_header(); if (get_option('rmm_image_importer_key') != urldecode($_GET['key'])) { echo '<div id="message" class="error">'; echo "<p><strong>Incorrect authentication key: you are not allowed to import images into this site.</strong></p></div>"; } else { echo '<br /><br />'; $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'])) { ?> <h2>Import from folder: <?php echo $data['folder']; ?></h2> <p> <?php importImage(realpath(str_replace('//', '/', ABSPATH . '../../' . $data['folder'])), $data['page']); ?> </p> <?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.<br /><br />'; } get_footer(); 

Solutions Collecting From Web of "Загрузка изображений в медиабиблиотеку не выполняется с исчерпанной памятью"

Несколько вещей

  • Используйте media_handle_sideload чтобы WordPress перемещал файлы в нужное место и проверял их для вас, создавал сообщение вложения и т. Д., Ни один из этих материалов руководства
  • Не запускайте это один раз и ожидайте, что он сделает все. Вы просто столкнетесь с одной и той же проблемой, но в дальнейшем импортируете. Если вы дадите ему бесконечную память, у вас будет проблема с исполнением ограничения времени, когда сценарий просто заканчивается
  • Обработайте 5 файлов за раз и повторно вызывайте его, пока ничего не осталось для обработки
  • Используйте команду WP CLI, не загружайте WordPress и не вызывайте ее из графического интерфейса. Вызовите его непосредственно из Cron и пропустите pinging URL-бизнес. Команды CLI получают неограниченное время для выполнения своей работы, и вы не можете вызывать их из браузера. Переменная GET с ключом становится совершенно ненужной.
  • Escape escape escape, вы эхом отдаете эти массивы и значения, считая, что они содержат, безопасно, но что, если я продержу там тег скрипта? Unable to delete file <script>...</script> after import. , Самый большой шаг в безопасности, который вы можете предпринять, делает наибольшую разницу, но наименее используемую

Команда прототипа WP CLI

Вот простая команда WP CLI, которая должна сделать трюк. Я не тестировал его, но все важные части там, я надеюсь, вы не полный новичок, когда дело доходит до PHP, и можете затянуть любые свободные винты или незначительные ошибки, никаких дополнительных знаний API не требуется.

Вы хотите включить его только в контексте WP CLI, например:

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

Измените, соответственно, сброс команды в functions.php темы и ожидание ее работы приведет к ошибкам, поскольку классы WP CLI загружаются только в командной строке, никогда не обрабатывая запрос браузера.

Применение:

wp mbimport run

Класс:

 <?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' ); 

Вызовите повторно из реального задания cron. Если вы не можете, то либо используйте WP Cron, либо имеете крючок на admin_init который проверяет переменную GET. Используйте код внутри команды run с некоторыми изменениями.

Когда WP CLI не является вариантом

Использование автономного файла PHP, который загружает WP, представляет собой угрозу безопасности и отличную цель для злоумышленников, если они хотят исчерпать ресурсы вашего сервера (или вызвать проблемы с дублированием, удалив URL-адрес несколько раз подряд).

Например:

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

Повторный вызов

Возможно, ваше внешнее обслуживание не может повторять это повторно. На что я говорю:

  • Не полагайтесь на внешнюю службу, если ваш собственный сервер вызывает ее независимо, даже если нет работы
  • Также будет работать стандартная задача WP Cron
  • Запускать его каждые 5 минут
  • Сделать вызов задачи сам, если есть еще что-то делать, используя неблокирующий запрос. Таким образом, он будет продолжать создавать новые экземпляры до тех пор, пока они не будут завершены, например

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

GUI?

Если вам нужен индикатор выполнения для пользовательского интерфейса на панели управления, просто подсчитайте, сколько файлов jpeg осталось в импортируемой папке. Если вам нужно настроить его, затем создайте пользовательский интерфейс и сохраните настройки в параметрах, а затем вытащите из параметров в сценарии CLI.

Считаете ли вы использование API REST?

Пройдите весь процесс и добавьте файлы через REST API. Вы можете сделать запрос POST на example.com/wp-json/wp/v2/media чтобы загрузить jpeg. Не требуется код на вашем сайте

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

Существует уже встроенная функция, созданная именно для этой цели. Вам не нужно писать стены кодов для загрузки изображений с вашего диска. media_sideload_image этого вы можете использовать media_sideload_image .

Эта функция загрузит ваши файлы, позаботится о имени файла, дате, ID и остальной части материала.

Я не тестировал это с абсолютным путем (ему нужен URL-адрес), но было бы легко преобразовать абсолютный путь к URL-адресам, основываясь на ваших навыках написания вышеприведенного скрипта.

 // 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'); } 

Это все, что вам нужно. Изображения должны быть прикреплены к сообщению, но потом вы можете отсоединить их.

Ваш сервер также может ограничивать общую загрузку, это не только то, что вы установили в коде. обратитесь к своему провайдеру или если у вас есть доступ к WHM, измените максимальную загрузку php для учетной записи (обычно это 8 ГБ), и вам придется изменить php.ini. Следующий URL-адрес был полезен для меня, когда мне приходилось делать то же самое:

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

Однако это не полное решение, если у сервера, на котором вы находитесь, есть предел.