WordPress自定义插件保存文章时无法传递$_FILES,仅能传递$_POST
解决WordPress自定义文章类型PDF上传时$_FILES为空的问题
问题背景
我正在开发一个WordPress自定义插件,目标是创建名为manual的自定义文章类型,从本地上传PDF文件并保存至wp_content/uploads/manuals/目录。目前保存manual类型文章时,savePost()函数能接收$_POST数据,但无法获取$_FILES数据,导致文件保存失败。
已排查确认:
- php.ini允许文件上传,文件大小符合服务器上限
- 目标文件夹权限设置正确,其他插件可正常向该目录上传PDF
- 本地及两家不同虚拟主机环境测试均出现相同问题
- 报错均指向
$_FILES为null,但$_POST数据中能看到["file"]=> string(9) "dummy.pdf"的记录
原有代码
//create the manuals custom post type function create_custom_post_type() { register_post_type( 'manuals', array( 'labels' => array( 'name' => __( 'Manuals' ), 'singular_name' => __( 'Manual' ) ), 'public' => true, 'has_archive' => true, 'supports' => array( 'title','custom_fields'), ) ); } add_action( 'init', 'create_custom_post_type' ); //add the manuals box to post page function manuals_meta_box() { add_meta_box( 'manuals_meta_box', 'Manuals PDF', 'display_manuals_meta_box', 'manuals', 'normal', 'high' ); } add_action( 'add_meta_boxes', 'manuals_meta_box' ); //display html on the meta box function display_manuals_meta_box( $manual ) { $saveLocation = ABSPATH.'/wp-content/uploads/manuals'; if (!file_exists(ABSPATH.'/wp-content/uploads/manuals')) { mkdir(ABSPATH.'/wp-content/uploads/manuals', 0777, true); } //see if this has been uploaded before $file = esc_html( get_post_meta( $manual->ID, 'file', true ) ); ?> <p><?php echo $file?$file:'File: Not set';?></p> <form method="POST" enctype="multipart/form-data"> <?php wp_nonce_field(plugin_basename(__FILE__), 'custom-nonce'); ?> <input type="file" name="file"> <input id="submit" class="submitBtn mfInput" name="submit" type="submit" placeholder=""> </form> <?php } //on post save, try to save the pdf selected function savePost( $manual_id, $manual ) { //security checks if(!wp_verify_nonce($_POST['custom-nonce'], plugin_basename(__FILE__))) { return $manual_id; } if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $manual_id; } if ( $manual->post_type == 'manuals' ){ if(!current_user_can('edit_page', $manual_id)) { return $manual_id; } } else { if(!current_user_can('edit_page', $manual_id)) { return $manual_id; } } 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'); } moveTheFile($_FILES['file']); } //move from tmp to actual file location function moveTheFile($file){ $moved = move_uploaded_file($file['tmp_name'], ABSPATH . "/wp-content/uploads/manuals/" . $file['name']); if( $moved ) { echo "Successfully uploaded"; } else { wp_die(var_dump($_FILES).'<br><br>'.var_dump($_POST)); } } add_action( 'save_post', 'savePost', 10, 2 );
报错信息
Trying to access array offset on value of type null Undefined array key "file" Trying to access array offset on value of type null move_uploaded_file(): Passing null to parameter #1 ($from) of type string is deprecated
问题根源
你在元框里单独写了一个独立的<form>,但WordPress后台的文章保存表单本身已经是一个<form>,HTML标准不允许表单嵌套,导致文件无法正确提交到save_post钩子的处理函数中。
修复后的完整代码
// 创建manuals自定义文章类型 function create_custom_post_type() { register_post_type( 'manuals', array( 'labels' => array( 'name' => __( '手册' ), 'singular_name' => __( '手册' ) ), 'public' => true, 'has_archive' => true, 'supports' => array( 'title','custom_fields'), ) ); } add_action( 'init', 'create_custom_post_type' ); // 为后台文章表单添加multipart属性,确保文件能被正确提交 function add_form_enctype() { echo ' enctype="multipart/form-data"'; } add_action('post_edit_form_tag', 'add_form_enctype'); // 添加手册PDF元框 function manuals_meta_box() { add_meta_box( 'manuals_meta_box', '手册PDF', 'display_manuals_meta_box', 'manuals', 'normal', 'high' ); } add_action( 'add_meta_boxes', 'manuals_meta_box' ); // 渲染元框内容 function display_manuals_meta_box( $manual ) { $saveLocation = ABSPATH.'/wp-content/uploads/manuals'; if (!file_exists($saveLocation)) { mkdir($saveLocation, 0755, true); // 用0755权限更安全,0777存在安全风险 } // 获取已上传的文件记录 $file = esc_html( get_post_meta( $manual->ID, 'file', true ) ); ?> <p><?php echo $file ? $file : '文件:未设置';?></p> <?php wp_nonce_field(plugin_basename(__FILE__), 'custom-nonce'); ?> <input type="file" name="manual_pdf"> <?php } // 保存文章时处理文件上传 function savePost( $manual_id, $manual ) { // 安全校验 if(!wp_verify_nonce($_POST['custom-nonce'], plugin_basename(__FILE__))) { return $manual_id; } if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $manual_id; } // 简化权限判断逻辑 if ( $manual->post_type !== 'manuals' || !current_user_can('edit_post', $manual_id) ) { return $manual_id; } // 检查是否有合法的文件上传 if(isset($_FILES['manual_pdf']) && $_FILES['manual_pdf']['error'] === UPLOAD_ERR_OK) { $uploaded_file = $_FILES['manual_pdf']; $target_dir = ABSPATH . "/wp-content/uploads/manuals/"; $target_file = $target_dir . basename($uploaded_file['name']); // 验证文件类型为PDF $file_type = strtolower(pathinfo($target_file, PATHINFO_EXTENSION)); if($file_type !== 'pdf') { wp_die('仅允许上传PDF文件'); } // 移动文件到目标目录 if(move_uploaded_file($uploaded_file['tmp_name'], $target_file)) { // 更新文章元数据,记录文件名 update_post_meta($manual_id, 'file', basename($uploaded_file['name'])); } else { wp_die('文件上传失败,请检查目录权限'); } } } add_action( 'save_post', 'savePost', 10, 2 );
关键修改点
- 移除元框内的独立
<form>标签,复用WordPress后台自带的文章提交表单 - 通过
post_edit_form_tag钩子为后台表单添加enctype="multipart/form-data"属性,确保文件能被正确提交 - 简化权限校验逻辑,合并重复判断
- 添加PDF文件类型验证,避免非目标文件上传
- 上传成功后更新文章元数据,保存文件名
- 将目录权限从0777改为更安全的0755(大部分服务器环境下足够)
内容的提问来源于stack exchange,提问作者user1129107
相关产品推荐
相关产品推荐

