You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修改点

  1. 移除元框内的独立<form>标签,复用WordPress后台自带的文章提交表单
  2. 通过post_edit_form_tag钩子为后台表单添加enctype="multipart/form-data"属性,确保文件能被正确提交
  3. 简化权限校验逻辑,合并重复判断
  4. 添加PDF文件类型验证,避免非目标文件上传
  5. 上传成功后更新文章元数据,保存文件名
  6. 将目录权限从0777改为更安全的0755(大部分服务器环境下足够)

内容的提问来源于stack exchange,提问作者user1129107

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:25:27