curl_file_create() 函数用于创建一个 CURLFile 对象,用于通过cURL上传文件。这是PHP 5.5+中推荐的文件上传方式,取代了旧的 @ 前缀语法。
curl_file_create ( string $filename [, string $mimetype = "" [, string $postname = "" ]] ) : CURLFile
| 参数 | 描述 |
|---|---|
| filename | 要上传的文件的路径。 |
| mimetype | 可选。文件的MIME类型。如果未指定,cURL会尝试自动检测。 |
| postname | 可选。文件在POST数据中的字段名称。如果未指定,使用文件名。 |
返回一个 CURLFile 对象。
// 检查文件是否存在
$filePath = '/path/to/your/file.jpg';
if (!file_exists($filePath)) {
die("文件不存在: {$filePath}");
}
// 创建CURLFile对象
$cfile = curl_file_create(
$filePath, // 文件路径
'image/jpeg', // MIME类型
'myfile.jpg' // POST字段名
);
// 准备POST数据
$postData = [
'file' => $cfile,
'user_id' => 123,
'description' => '上传文件示例'
];
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/post',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: multipart/form-data'
]
]);
// 执行请求
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'cURL错误: ' . curl_error($ch);
} else {
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP状态码: {$httpCode}\n";
echo "响应内容长度: " . strlen($response) . " 字节\n";
}
curl_close($ch);
// 准备多个文件
$files = [
[
'path' => '/path/to/image1.jpg',
'field' => 'images[]',
'mime' => 'image/jpeg'
],
[
'path' => '/path/to/image2.png',
'field' => 'images[]',
'mime' => 'image/png'
],
[
'path' => '/path/to/document.pdf',
'field' => 'documents',
'mime' => 'application/pdf'
]
];
// 构建POST数据
$postData = [
'user_id' => 456,
'username' => 'john_doe'
];
foreach ($files as $file) {
if (file_exists($file['path'])) {
$cfile = curl_file_create(
$file['path'],
$file['mime'],
basename($file['path'])
);
$postData[$file['field']] = $cfile;
} else {
echo "文件不存在: {$file['path']}\n";
}
}
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/post',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30
]);
// 执行请求
$response = curl_exec($ch);
if ($response === false) {
echo 'cURL错误: ' . curl_error($ch);
} else {
$result = json_decode($response, true);
if ($result && isset($result['files'])) {
echo "成功上传 " . count($result['files']) . " 个文件\n";
foreach ($result['files'] as $field => $filename) {
echo " {$field}: {$filename}\n";
}
}
}
curl_close($ch);
class FileUploader {
private $ch;
private $uploadedBytes = 0;
private $totalBytes = 0;
public function __construct($url) {
$this->ch = curl_init();
curl_setopt_array($this->ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => [$this, 'progressCallback'],
CURLOPT_TIMEOUT => 0, // 无超时限制,适用于大文件
CURLOPT_HTTPHEADER => [
'Expect: ' // 防止100-continue等待
]
]);
}
public function progressCallback($resource, $downloadSize, $downloaded, $uploadSize, $uploaded) {
if ($uploadSize > 0) {
$this->totalBytes = $uploadSize;
$this->uploadedBytes = $uploaded;
$percentage = ($uploadSize > 0) ? round(($uploaded / $uploadSize) * 100, 2) : 0;
echo "上传进度: {$percentage}% ({$uploaded}/{$uploadSize} 字节)\r";
if ($uploaded >= $uploadSize) {
echo "\n上传完成!\n";
}
}
return 0;
}
public function uploadFile($filePath, $fieldName = 'file', $additionalData = []) {
if (!file_exists($filePath)) {
throw new Exception("文件不存在: {$filePath}");
}
// 获取文件信息
$fileSize = filesize($filePath);
$fileName = basename($filePath);
$mimeType = mime_content_type($filePath) ?: 'application/octet-stream';
echo "开始上传文件: {$fileName} ({$fileSize} 字节)\n";
// 创建CURLFile
$cfile = curl_file_create($filePath, $mimeType, $fileName);
// 构建POST数据
$postData = array_merge($additionalData, [
$fieldName => $cfile
]);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $postData);
// 执行上传
$startTime = microtime(true);
$response = curl_exec($this->ch);
$endTime = microtime(true);
$uploadTime = $endTime - $startTime;
$speed = ($fileSize > 0 && $uploadTime > 0) ? round($fileSize / $uploadTime / 1024, 2) : 0;
echo "\n上传统计:\n";
echo " 文件大小: {$fileSize} 字节\n";
echo " 上传时间: " . round($uploadTime, 2) . " 秒\n";
echo " 平均速度: {$speed} KB/秒\n";
if ($response === false) {
throw new Exception('上传失败: ' . curl_error($this->ch));
}
return [
'response' => $response,
'http_code' => curl_getinfo($this->ch, CURLINFO_HTTP_CODE),
'upload_time' => $uploadTime,
'speed_kbps' => $speed
];
}
public function __destruct() {
if ($this->ch) {
curl_close($this->ch);
}
}
}
// 使用示例
try {
$uploader = new FileUploader('https://httpbin.org/post');
$result = $uploader->uploadFile(
'/path/to/large/file.zip',
'uploaded_file',
[
'user_id' => 789,
'category' => 'backup'
]
);
echo "上传成功! HTTP状态码: {$result['http_code']}\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
class SecureFileUploader {
private $allowedTypes = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
'application/pdf' => 'pdf',
'text/plain' => 'txt'
];
private $maxFileSize = 10 * 1024 * 1024; // 10MB
public function validateFile($filePath) {
// 检查文件是否存在
if (!file_exists($filePath)) {
return ['valid' => false, 'error' => '文件不存在'];
}
// 检查文件大小
$fileSize = filesize($filePath);
if ($fileSize > $this->maxFileSize) {
return [
'valid' => false,
'error' => "文件大小超过限制: {$fileSize} > {$this->maxFileSize}"
];
}
// 检查MIME类型
$mimeType = mime_content_type($filePath);
if (!$mimeType || !isset($this->allowedTypes[$mimeType])) {
return [
'valid' => false,
'error' => "不允许的文件类型: {$mimeType}"
];
}
// 检查文件扩展名
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$expectedExtension = $this->allowedTypes[$mimeType];
if ($extension !== $expectedExtension) {
return [
'valid' => false,
'error' => "文件扩展名不匹配: {$extension} != {$expectedExtension}"
];
}
// 安全检查:尝试读取文件前几个字节
$handle = fopen($filePath, 'rb');
$firstBytes = fread($handle, 256);
fclose($handle);
// 简单的恶意内容检查(实际应用中应该使用更严格的安全检查)
if (preg_match('/<\?php|eval\(|base64_decode/i', $firstBytes)) {
return ['valid' => false, 'error' => '文件可能包含恶意内容'];
}
return [
'valid' => true,
'size' => $fileSize,
'mime_type' => $mimeType,
'extension' => $extension,
'filename' => basename($filePath)
];
}
public function uploadFile($filePath, $uploadUrl, $fieldName = 'file', $extraData = []) {
// 验证文件
$validation = $this->validateFile($filePath);
if (!$validation['valid']) {
return [
'success' => false,
'error' => $validation['error']
];
}
// 创建CURLFile
$cfile = curl_file_create(
$filePath,
$validation['mime_type'],
$validation['filename']
);
// 准备POST数据
$postData = array_merge($extraData, [
$fieldName => $cfile,
'file_info' => json_encode([
'original_name' => $validation['filename'],
'size' => $validation['size'],
'type' => $validation['mime_type']
])
]);
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $uploadUrl,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => [
'Content-Type: multipart/form-data',
'X-File-Size: ' . $validation['size']
]
]);
// 执行上传
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($response === false) {
return [
'success' => false,
'error' => "上传失败: {$error}",
'http_code' => $httpCode
];
}
return [
'success' => true,
'response' => $response,
'http_code' => $httpCode,
'file_info' => $validation
];
}
public function setAllowedTypes($types) {
$this->allowedTypes = $types;
}
public function setMaxFileSize($bytes) {
$this->maxFileSize = $bytes;
}
}
// 使用示例
$uploader = new SecureFileUploader();
// 设置允许的文件类型
$uploader->setAllowedTypes([
'image/jpeg' => 'jpg',
'image/png' => 'png',
'application/pdf' => 'pdf'
]);
// 设置最大文件大小为5MB
$uploader->setMaxFileSize(5 * 1024 * 1024);
// 尝试上传文件
$result = $uploader->uploadFile(
'/path/to/test.jpg',
'https://httpbin.org/post',
'user_file',
['user_id' => 1001, 'category' => 'profile']
);
if ($result['success']) {
echo "文件上传成功!\n";
echo "HTTP状态码: {$result['http_code']}\n";
echo "文件信息:\n";
print_r($result['file_info']);
} else {
echo "文件上传失败: {$result['error']}\n";
}
function uploadFromMemory($data, $filename, $mimeType = 'application/octet-stream') {
// 创建临时文件
$tempFile = tempnam(sys_get_temp_dir(), 'curl_upload_');
// 将数据写入临时文件
file_put_contents($tempFile, $data);
// 创建CURLFile
$cfile = curl_file_create($tempFile, $mimeType, $filename);
// 准备POST数据
$postData = [
'file' => $cfile,
'filename' => $filename,
'size' => strlen($data)
];
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/post',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_RETURNTRANSFER => true
]);
// 执行请求
$response = curl_exec($ch);
$error = curl_error($ch);
// 清理临时文件
unlink($tempFile);
if ($response === false) {
throw new Exception("上传失败: {$error}");
}
curl_close($ch);
return $response;
}
// 使用示例:上传生成的图像
$imageData = generateSampleImage();
$result = uploadFromMemory($imageData, 'sample.png', 'image/png');
echo "内存文件上传完成,响应长度: " . strlen($result) . " 字节\n";
// 辅助函数:生成示例图像
function generateSampleImage() {
$image = imagecreatetruecolor(200, 100);
$backgroundColor = imagecolorallocate($image, 70, 130, 180);
$textColor = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $backgroundColor);
imagestring($image, 5, 50, 40, 'Sample Image', $textColor);
ob_start();
imagepng($image);
$imageData = ob_get_clean();
imagedestroy($image);
return $imageData;
}
CURLFile 对象有以下可用方法:
| 方法 | 描述 |
|---|---|
getFilename() |
获取文件路径 |
getMimeType() |
获取MIME类型 |
getPostFilename() |
获取POST字段中的文件名 |
setMimeType($mime) |
设置MIME类型 |
setPostFilename($name) |
设置POST字段中的文件名 |
// 创建CURLFile对象
$cfile = curl_file_create('/path/to/file.jpg', 'image/jpeg', 'upload.jpg');
// 使用方法获取信息
echo "文件路径: " . $cfile->getFilename() . "\n";
echo "MIME类型: " . $cfile->getMimeType() . "\n";
echo "POST文件名: " . $cfile->getPostFilename() . "\n";
// 修改属性
$cfile->setMimeType('image/png');
$cfile->setPostFilename('new_name.png');
echo "修改后MIME类型: " . $cfile->getMimeType() . "\n";
echo "修改后POST文件名: " . $cfile->getPostFilename() . "\n";
| 方式 | PHP版本 | 示例 | 说明 |
|---|---|---|---|
| 旧语法 (@前缀) | PHP 5.5 之前 | CURLOPT_POSTFIELDS => ['file' => '@file.jpg'] |
已废弃,不推荐使用 |
| 新语法 (CURLFile) | PHP 5.5+ | CURLOPT_POSTFIELDS => ['file' => curl_file_create('file.jpg')] |
推荐使用 |
curl_file_create() 函数需要PHP 5.5.0或更高版本,并且需要cURL支持。
CURLFile 而不是旧的 @ 语法A: CURLFile 是PHP 5.5+中推荐的文件上传方式,它提供了更好的类型安全性和可维护性。旧的 @ 语法已废弃,可能在未来的PHP版本中被移除。
A: 对于大文件上传:
1. 增加PHP的 memory_limit 和 upload_max_filesize 设置
2. 使用 CURLOPT_TIMEOUT 设置适当的超时时间
3. 实现进度回调函数以监控上传进度
4. 考虑使用分块上传或断点续传
A: 使用 CURLOPT_PROGRESSFUNCTION 选项设置进度回调函数。回调函数会接收上传的字节数和总字节数,从而计算上传进度。