我正在尝试使用通过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, "[gallery ") + strlen("[gallery ");
$end = strpos(substr($content, $start), "]");
$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();
问题是,无论我做什么,只要两个图像出现以下错误,脚本就会失败:
致命错误:在/home/forge/morselandcompany中,允许的内存大小为1073741824字节(尝试分配28672字节)。com/public/wordpress/wp包括/wp数据库。php在线1841
我在wordpress和PHP中将内存限制设置为1024M。我真的不明白为什么这个脚本需要超过128M。如何优化此脚本以正常工作?(平均图像大小为800kB。)
使用BlackFire进行一些初始调试。io建议使用以下内存占用:-wpdb->查询:3.2MB,调用31次-mysqli\\u fetch\\u对象:1.89MB,调用595次-在wp设置中运行\\u init()。php:5.4MB,称为onceIn total blackfire,表明运行此脚本需要超过8MB!
我还测试了所有禁用的插件,结果也是一样的。
我正在运行-PHP 7.1-Ubuntu 16.04-DigitalOcean VPS(1个CPU,1GB RAM)
-Wordpress 4.8-NGINX 1.11.5
谢谢你的帮助!
Update: 为了确保完整性,以便其他人可以利用与get\\u post和wp\\u update\\u 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, "[gallery ") + strlen("[gallery ");
$end = strpos(substr($content, $start), "]");
$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();
最合适的回答,由SO网友:Tom J Nowell 整理而成
几件事
使用media_handle_sideload
因此,WordPress会将文件移动到正确的位置,并为您验证文件,创建附件帖子等,所有这些手动操作都不会运行一次,希望它能完成所有操作。您只会遇到同样的问题,但会进一步进入导入。如果给它无限大的内存,则会出现执行时间限制问题,脚本只会耗尽时间,一次处理5个文件,并重复调用run it,直到没有剩余的文件可处理。请使用WP CLI命令,不要引导WordPress并从GUI调用它。直接从Cron调用它,跳过ping URL业务。CLI命令的工作时间是无限的,您无法从浏览器中调用它们。带有键的GET变量变得完全没有必要了退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出退出?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来触发复制问题),则WP是一个很好的攻击目标。
例如:
// 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如果您想在仪表板中为UI设置进度表,只需计算文件夹中要导入的jpeg文件的数量。如果需要对其进行配置,请构建UI并将设置保存在options中,然后从CLI脚本中的options中提取。
您是否考虑过使用REST API
绕过整个过程,通过REST API添加文件。您可以向
example.com/wp-json/wp/v2/media
上载JPEG。站点上无需任何代码
https://stackoverflow.com/questions/37432114/wp-rest-api-upload-image