PHP7中使用cURL上传文件的完整指南
PHP7中使用cURL上传文件的完整指南
一、基础文件上传实现
1.1 单文件上传实现
<?php
// 目标上传URL
$uploadUrl = 'https://example.com/upload.php';
// 准备上传文件(必须使用绝对路径)
$filePath = realpath('test_file.txt');
// 创建CURLFile对象
$postData = [
    'userfile' => new CURLFile($filePath, 'text/plain', 'custom_filename.txt'),
    'additional_field' => 'extra_data'
];
// 初始化cURL会话
$ch = curl_init();
// 设置cURL选项
curl_setopt_array($ch, [
    CURLOPT_URL => $uploadUrl,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $postData,
    CURLOPT_HTTPHEADER => [
        'User-Agent: PHP cURL File Upload'
    ]
]);
// 执行请求并获取响应
$response = curl_exec($ch);
// 检查错误
if (curl_errno($ch)) {
    throw new RuntimeException('cURL Error: ' . curl_error($ch));
}
// 关闭会话
curl_close($ch);
// 处理响应
echo $response;
关键点说明:
- CURLFile类替代了旧的- @语法(PHP5.5+)
- realpath()确保使用绝对路径
- 可自定义MIME类型和上传后的文件名
二、高级上传功能
2.1 多文件同时上传
$postData = [
    'avatar' => new CURLFile(realpath('user.jpg'), 'image/jpeg'),
    'documents' => [
        new CURLFile(realpath('doc1.pdf'), 'application/pdf'),
        new CURLFile(realpath('doc2.pdf'), 'application/pdf')
    ],
    'user_id' => 12345
];
2.2 上传进度监控
// 进度回调函数
function progressCallback($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) {
    if ($uploadSize > 0) {
        $progress = round(($uploaded / $uploadSize) * 100);
        echo "上传进度: {$progress}%\n";
    }
}
// 设置进度回调
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progressCallback');
三、服务器端接收处理
3.1 基础接收脚本
<?php
header('Content-Type: application/json');
$response = [
    'status' => 'success',
    'files' => $_FILES,
    'post_data' => $_POST
];
// 文件保存处理
foreach ($_FILES as $field => $file) {
    if ($file['error'] === UPLOAD_ERR_OK) {
        $targetPath = 'uploads/' . basename($file['name']);
        move_uploaded_file($file['tmp_name'], $targetPath);
    }
}
echo json_encode($response, JSON_PRETTY_PRINT);
3.2 安全增强版
<?php
header('Content-Type: application/json');
function sanitizeFilename($filename) {
    return preg_replace('/[^a-zA-Z0-9\-\._]/', '', $filename);
}
$response = ['status' => 'error'];
$allowedTypes = ['image/jpeg', 'application/pdf'];
try {
    if (empty($_FILES)) {
        throw new RuntimeException('没有文件被上传');
    }
    
    foreach ($_FILES as $file) {
        if (!in_array($file['type'], $allowedTypes)) {
            throw new RuntimeException('不允许的文件类型: ' . $file['type']);
        }
        
        $safeName = sanitizeFilename($file['name']);
        $targetPath = 'secure_uploads/' . uniqid() . '_' . $safeName;
        
        if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
            throw new RuntimeException('文件保存失败');
        }
    }
    
    $response['status'] = 'success';
    $response['saved_files'] = glob('secure_uploads/*');
    
} catch (RuntimeException $e) {
    $response['message'] = $e->getMessage();
}
echo json_encode($response);
四、错误处理与调试
4.1 完整错误处理方案
$ch = curl_init();
// ... 设置其他选项 ...
try {
    $response = curl_exec($ch);
    
    if ($response === false) {
        throw new RuntimeException('cURL错误: ' . curl_error($ch));
    }
    
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($httpCode >= 400) {
        throw new RuntimeException("HTTP错误: {$httpCode}");
    }
    
    $data = json_decode($response, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        throw new RuntimeException('JSON解析错误: ' . json_last_error_msg());
    }
    
    // 处理成功响应
    print_r($data);
    
} catch (RuntimeException $e) {
    error_log('文件上传失败: ' . $e->getMessage());
    echo '上传失败,请稍后重试';
} finally {
    curl_close($ch);
}
4.2 调试技巧
// 记录完整请求信息
file_put_contents('curl_debug.log', print_r([
    'url' => $uploadUrl,
    'headers' => get_headers($uploadUrl),
    'post_data' => $postData,
    'response' => $response,
    'curl_info' => curl_getinfo($ch)
], true), FILE_APPEND);
五、性能优化建议
- 
启用HTTP/2: curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
- 
连接复用: curl_setopt($ch, CURLOPT_FORBID_REUSE, false); curl_setopt($ch, CURLOPT_FRESH_CONNECT, false);
- 
超时设置: curl_setopt_array($ch, [ CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30 ]);
- 
批量上传优化: - 使用curl_multi_init()处理并行上传
- 压缩大文件后再上传
 
- 使用
六、安全注意事项
- 
SSL验证: curl_setopt_array($ch, [ CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_CAINFO => '/path/to/cacert.pem' ]);
- 
文件类型检查: function isAllowedFile($tmpPath) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $tmpPath); finfo_close($finfo); $allowed = ['image/jpeg', 'image/png']; return in_array($mime, $allowed); }
- 
敏感数据保护: - 不要在上传内容中包含敏感信息
- 使用临时生成的访问令牌
 
七、完整封装类示例
class FileUploader {
    private $ch;
    private $options = [
        'timeout' => 30,
        'verify_ssl' => true
    ];
    
    public function __construct(array $options = []) {
        $this->options = array_merge($this->options, $options);
        $this->ch = curl_init();
    }
    
    public function upload($url, $files, $postFields = []) {
        $postData = [];
        
        // 准备文件字段
        foreach ($files as $field => $file) {
            if (is_array($file['tmp_name'])) {
                // 处理多文件上传
                foreach ($file['tmp_name'] as $i => $tmpName) {
                    $postData[$field . '[' . $i . ']'] = new CURLFile(
                        $tmpName,
                        $file['type'][$i],
                        $file['name'][$i]
                    );
                }
            } else {
                $postData[$field] = new CURLFile(
                    $file['tmp_name'],
                    $file['type'],
                    $file['name']
                );
            }
        }
        
        // 合并普通POST字段
        $postData = array_merge($postData, $postFields);
        
        // 配置cURL
        curl_setopt_array($this->ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $postData,
            CURLOPT_TIMEOUT => $this->options['timeout'],
            CURLOPT_SSL_VERIFYPEER => $this->options['verify_ssl']
        ]);
        
        $response = curl_exec($this->ch);
        
        if ($response === false) {
            throw new RuntimeException('Upload failed: ' . curl_error($this->ch));
        }
        
        return $response;
    }
    
    public function __destruct() {
        curl_close($this->ch);
    }
}
// 使用示例
$uploader = new FileUploader();
$response = $uploader->upload(
    'https://example.com/upload',
    [
        'document' => $_FILES['document']
    ],
    [
        'user_id' => 123,
        'api_key' => 'abc123'
    ]
);