|

分享源码
界面截图: |
|
是否带模块: |
纯源码 |
备注说明: |
- |

一、image_api.php 支持主流的图片格式上传
二、File_api.php 支持所有文件上传
E源码:
变量名 | 类 型 | 静态 | 数组 | 备 注 | 分隔符 | 文本型 | | | 时间戳 | 文本型 | | | API_KEY | 文本型 | | | 头信息 | 文本型 | | | 文件名 | 文本型 | | | 表单数据 | 字节集 | | | 上传结果 | 字节集 | | | json | 类_json | | | api地址 | 文本型 | | |
api地址 = “http://127.0.0.1/image_api.php” 分隔符 = “---------------------------” + 取十六进制文本 (取随机数 (100000000, 999999999 )) 时间戳 = 时间_到时间戳 (取现行时间 (), 真, )文件名 = 文件_取文件名 (图片地址, 真)API_KEY = 校验_取md5 (到字节集 (时间戳 + “QQ516221198_QUN1071098978” + 文件名 ), , ) 头信息 = “X-API-TIMESTAMP: ” + 时间戳 + #换行符 + “X-API-SIGNATURE: ” + API_KEY + #换行符 + “Content-Type: multipart/form-data; boundary=” + 分隔符 头信息 = 头信息 + #换行符 + “User-Agent: Mozilla/5.0” 表单数据 = 到字节集 (“--” + 分隔符 + #换行符 + “Content-Disposition: form-data; name=” + #引号 + “image” + #引号 + “; filename=” + #引号 + 文件名 + #引号 + #换行符 + “Content-Type: application/octet-stream” + #换行符 + #换行符 ) + 读入文件 (图片地址 ) + 到字节集 ( #换行符 + “--” + 分隔符 + “--”) 上传结果 = 编码_编码转换对象 (网页_访问_对象 (api地址, 1, , , , 头信息, , , , 表单数据, , , , , , , , , ), , , )json. 解析 (到文本 (上传结果 ), , )调试输出 (到文本 (上传结果 )) 如果 (json. 取属性对象 (“success”) = “true”) 图片地址 = json. 取通用属性 (“['image_url']”, ) 返回 (真) 图片地址 = “” 错误提示 = json. 取通用属性 (“message”, )返回 (假)变量名 | 类 型 | 静态 | 数组 | 备 注 | 分隔符 | 文本型 | | | 时间戳 | 文本型 | | | API_KEY | 文本型 | | | 头信息 | 文本型 | | | 文件名 | 文本型 | | | 表单数据 | 字节集 | | | 上传结果 | 字节集 | | | json | 类_json | | | api地址 | 文本型 | | |
api地址 = “http://127.0.0.1/File_api.php” 分隔符 = “---------------------------” + 取十六进制文本 (取随机数 (100000000, 999999999 )) 时间戳 = 时间_到时间戳 (取现行时间 (), 真, )文件名 = 文件_取文件名 (图片地址, 真)API_KEY = 校验_取md5 (到字节集 (时间戳 + “QQ516221198_QUN1071098978” + 文件名 ), , ) 头信息 = “X-API-TIMESTAMP: ” + 时间戳 + #换行符 + “X-API-SIGNATURE: ” + API_KEY + #换行符 + “Content-Type: multipart/form-data; boundary=” + 分隔符 头信息 = 头信息 + #换行符 + “User-Agent: Mozilla/5.0” 表单数据 = 到字节集 (“--” + 分隔符 + #换行符 + “Content-Disposition: form-data; name=” + #引号 + “file” + #引号 + “; filename=” + #引号 + 文件名 + #引号 + #换行符 + “Content-Type: application/octet-stream” + #换行符 + #换行符 ) + 读入文件 (图片地址 ) + 到字节集 ( #换行符 + “--” + 分隔符 + “--”) 上传结果 = 编码_编码转换对象 (网页_访问_对象 (api地址, 1, , , , 头信息, , , , 表单数据, , , , , , , , , ), , , )json. 解析 (到文本 (上传结果 ), , )调试输出 (到文本 (上传结果 )) 如果 (json. 取属性对象 (“success”) = “true”) 图片地址 = json. 取通用属性 (“['file_url']”, ) 返回 (真) 图片地址 = “” 错误提示 = json. 取通用属性 (“message”, )返回 (假)
PHP 图片接口源码
[PHP] 纯文本查看 复制代码 <?php
// 设置响应头为JSON格式
header("Content-Type: application/json; charset=utf-8");
// 允许跨域请求
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-API-TIMESTAMP, X-API-SIGNATURE");
// 安全配置 - 请在实际使用时修改为强密钥
$apiKey = 'QQ516221198_QUN1071098978'; // 替换为你的API密钥
$allowedTimeWindow = 30; // 允许的请求时间窗口(秒),防止重放攻击
// 根据文件扩展名获取MIME类型
function getMimeTypeFromExtension($filename) {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$mimeTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'ico' => 'image/x-icon',
'svg' => 'image/svg+xml',
'tiff' => 'image/tiff',
'tif' => 'image/tiff'
];
return isset($mimeTypes[$extension]) ? $mimeTypes[$extension] : 'application/octet-stream';
}
// 检查文件签名以验证文件内容的真实性
function verifyImageSignature($filePath) {
try {
// 读取文件头字节进行基本验证
$fileHeader = file_get_contents($filePath, false, null, 0, 8);
// 检查JPG文件签名 (FF D8)
if (substr($fileHeader, 0, 2) === "\xFF\xD8") {
return true;
}
// 检查PNG文件签名
if (substr($fileHeader, 0, 8) === "\x89PNG\r\n\x1A\n") {
return true;
}
} catch (Exception $e) {
error_log('文件签名验证错误: ' . $e->getMessage());
}
return false;
}
// 尝试转换非标准图片为标准JPG格式的函数
function convertToStandardJPG($sourcePath) {
try {
// 检查GD库是否可用
if (!extension_loaded('gd')) {
error_log('GD库未加载,无法转换图片');
return false;
}
// 创建临时转换路径
$targetPath = $sourcePath . '_converted.jpg';
// 尝试使用GD库打开图片
$image = @imagecreatefromstring(file_get_contents($sourcePath));
if ($image === false) {
error_log('无法从字符串创建图片');
return false;
}
// 将图片保存为标准JPG格式
$success = imagejpeg($image, $targetPath, 90);
imagedestroy($image);
return $success ? $targetPath : false;
} catch (Exception $e) {
error_log('图片转换错误: ' . $e->getMessage());
return false;
}
}
// 验证函数 - 只保留上传功能的验证
function validateRequest() {
try {
global $apiKey, $allowedTimeWindow;
// 获取请求头中的时间戳和签名
$timestamp = isset($_SERVER['HTTP_X_API_TIMESTAMP']) ? $_SERVER['HTTP_X_API_TIMESTAMP'] : '';
$signature = isset($_SERVER['HTTP_X_API_SIGNATURE']) ? $_SERVER['HTTP_X_API_SIGNATURE'] : '';
// 检查必要的头信息
if (empty($timestamp) || empty($signature)) {
http_response_code(401);
$response = [
'success' => false,
'message' => '缺少必要的信息:时间戳或签名',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 验证时间戳,防止重放攻击
$currentTime = time();
if (abs($currentTime - $timestamp) > $allowedTimeWindow) {
http_response_code(401);
$response = [
'success' => false,
'message' => '请求已过期,时间差:' . abs($currentTime - $timestamp) . '秒',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 获取上传的文件名
$fileName = isset($_FILES['image']['name']) ? $_FILES['image']['name'] : '';
// 检查文件名是否存在
if (empty($fileName)) {
http_response_code(401);
$response = [
'success' => false,
'message' => '无法获取文件名',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 生成预期的签名:时间戳+API密钥+文件名的MD5值
$expectedSignature = md5($timestamp . $apiKey . $fileName);
// 验证签名
if ($signature !== $expectedSignature) {
http_response_code(401);
$response = [
'success' => false,
'message' => '无效的签名',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
} catch (Exception $e) {
http_response_code(500);
$response = [
'success' => false,
'message' => '验证过程中发生错误:' . $e->getMessage(),
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
}
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'Preflight request successful', 'image_url' => ''], JSON_UNESCAPED_SLASHES);
exit;
}
// 处理非POST请求
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => '不支持的请求方法', 'image_url' => ''], JSON_UNESCAPED_SLASHES);
exit;
}
// 全局变量用于跟踪转换状态
global $isConverted, $convertedTmpPath, $originalMimeType;
$isConverted = false;
$convertedTmpPath = null;
$originalMimeType = null;
// 图片配置
$uploadDir = 'uploads'; // 简化目录名,不带斜杠
$maxFileSize = 5 * 1024 * 1024; // 最大文件大小:5MB
// 确保上传目录存在,如果不存在则创建
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
@chmod($uploadDir, 0777);
}
// 确保目录路径格式正确
$uploadDir = rtrim($uploadDir, '/\\') . DIRECTORY_SEPARATOR;
// 获取当前日期,用于创建日期目录
$currentDate = date('Y-m-d');
$dateDir = $uploadDir . $currentDate . DIRECTORY_SEPARATOR;
// 确保日期目录存在,如果不存在则创建
if (!file_exists($dateDir)) {
mkdir($dateDir, 0777, true);
@chmod($dateDir, 0777);
}
// 获取当前API的基础URL(用于生成图片URL)
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http";
$host = $_SERVER['HTTP_HOST'];
$port = $_SERVER['SERVER_PORT'];
// 标准端口不需要重复添加
$standardPorts = ['80', '443'];
$portSuffix = in_array($port, $standardPorts) ? '' : ":$port";
// 确保host不包含端口号,避免重复
if (strpos($host, ':') !== false) {
$host = explode(':', $host)[0];
}
$baseUrl = "$protocol://$host$portSuffix{$_SERVER['REQUEST_URI']}";
$baseUrl = dirname($baseUrl) . '/';
try {
// 检查是否收到文件
if (!isset($_FILES['image'])) {
http_response_code(400);
$response = [
'success' => false,
'message' => '没有收到上传的文件',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 验证请求签名
validateRequest();
$file = $_FILES['image'];
// 检查文件上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
$response = [
'success' => false,
'message' => '文件上传错误,错误代码: ' . $file['error'],
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 检查文件大小
if ($file['size'] > $maxFileSize) {
http_response_code(400);
$response = [
'success' => false,
'message' => '文件过大,最大允许5MB',
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 检查文件类型(MIME类型验证)
$mimeType = '';
if (class_exists('finfo')) {
try {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($file['tmp_name']);
} catch (Exception $e) {
// 如果finfo失败,使用备用方法
$mimeType = getMimeTypeFromExtension($file['name']);
}
} elseif (function_exists('mime_content_type')) {
// 如果mime_content_type函数存在,使用它
$mimeType = mime_content_type($file['tmp_name']);
} else {
// 如果都不存在,使用文件扩展名推断MIME类型
$mimeType = getMimeTypeFromExtension($file['name']);
}
// 获取文件扩展名进行辅助验证
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico', 'svg', 'tiff', 'tif'];
// 添加更多可能的图片MIME类型
$supportedTypes = [
'image/jpeg', 'image/jpg', 'image/png', 'image/x-png', 'application/png', 'image/vnd.png',
'image/gif', 'image/bmp', 'image/webp', 'image/x-icon', 'image/svg+xml', 'image/tiff',
'image/x-ms-bmp', 'image/vnd.microsoft.icon', 'image/x-windows-bmp'
];
// 主验证逻辑:优先MIME类型验证,MIME类型不匹配时进行文件签名验证
$isValidImage = in_array($mimeType, $supportedTypes);
// 保存原始MIME类型用于响应
$originalMimeType = $mimeType;
// 如果MIME类型不匹配但扩展名为允许的类型,进行文件签名验证
if (!$isValidImage && in_array($fileExtension, $allowedExtensions)) {
$isValidImage = verifyImageSignature($file['tmp_name']);
}
// 如果MIME类型不匹配且不是有效图片签名,但文件扩展名是图片类型,尝试转换
if (!$isValidImage && in_array($fileExtension, $allowedExtensions)) {
// 尝试转换为标准JPG
$convertedPath = convertToStandardJPG($file['tmp_name']);
if ($convertedPath) {
// 验证转换后的文件
$convertedMimeType = '';
if (class_exists('finfo')) {
try {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$convertedMimeType = $finfo->file($convertedPath);
} catch (Exception $e) {
$convertedMimeType = getMimeTypeFromExtension($convertedPath);
}
} elseif (function_exists('mime_content_type')) {
$convertedMimeType = mime_content_type($convertedPath);
} else {
$convertedMimeType = getMimeTypeFromExtension($convertedPath);
}
if (in_array($convertedMimeType, $supportedTypes)) {
// 替换原始临时文件路径
$file['tmp_name'] = $convertedPath;
$mimeType = $convertedMimeType;
$fileExtension = 'jpg';
$isValidImage = true;
// 设置转换状态
$isConverted = true;
$convertedTmpPath = $convertedPath;
} else {
// 转换失败,删除临时文件
@unlink($convertedPath);
}
}
}
if (!$isValidImage) {
http_response_code(400);
$response = [
'success' => false,
'message' => '不支持的文件类型,只允许JPG、PNG。检测到类型:' . $mimeType,
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 生成唯一的文件名
$originalExtension = pathinfo($file['name'], PATHINFO_EXTENSION);
$extension = !empty($originalExtension) ? strtolower($originalExtension) : 'jpg';
$filename = bin2hex(random_bytes(16)) . '.' . $extension;
// 构建目标路径,使用日期目录
$targetPath = $dateDir . $filename;
// 作为最后的备选方案,尝试使用当前脚本所在目录
$scriptDir = __DIR__ . DIRECTORY_SEPARATOR;
$backupTargetPath = $scriptDir . 'uploads_' . basename($targetPath);
// 移动上传的文件并设置权限
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
// 设置文件权限
@chmod($targetPath, 0644);
// 返回成功信息和图片URL
$webPath = 'uploads/' . $currentDate . '/' . $filename;
$fullImageUrl = $baseUrl . $webPath;
// 构建简化的响应数据
$message = $isConverted ? '图片上传成功(已转换为标准格式)' : '图片上传成功';
if ($isConverted) {
$message .= ',原始类型:' . $originalMimeType;
}
$response = [
'success' => true,
'message' => $message,
'image_url' => $fullImageUrl
];
// 确保JSON格式正确且不转义斜杠
$jsonResponse = json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
echo $jsonResponse;
// 如果有转换的临时文件,尝试删除
if ($isConverted && isset($convertedTmpPath) && file_exists($convertedTmpPath)) {
@unlink($convertedTmpPath);
}
} else {
// 尝试备选路径
if (move_uploaded_file($file['tmp_name'], $backupTargetPath)) {
@chmod($backupTargetPath, 0644);
// 备选路径也使用日期目录结构
$backupDateDir = $scriptDir . 'uploads_' . $currentDate . DIRECTORY_SEPARATOR;
if (!file_exists($backupDateDir)) {
mkdir($backupDateDir, 0777, true);
@chmod($backupDateDir, 0777);
}
$newBackupTargetPath = $backupDateDir . basename($backupTargetPath);
// 移动到日期子目录
rename($backupTargetPath, $newBackupTargetPath);
// 构建完整的图片URL
$fullImageUrl = $baseUrl . 'uploads_' . $currentDate . '/' . basename($backupTargetPath);
// 构建简化的响应数据
$message = $isConverted ? '图片上传成功(已转换为标准格式,使用备选路径)' : '图片上传成功(使用备选路径)';
if ($isConverted) {
$message .= ',原始类型:' . $originalMimeType;
}
$response = [
'success' => true,
'message' => $message,
'image_url' => $fullImageUrl
];
// 确保JSON格式正确且不转义斜杠
$jsonResponse = json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
echo $jsonResponse;
// 如果有转换的临时文件,尝试删除
if ($isConverted && isset($convertedTmpPath) && file_exists($convertedTmpPath)) {
@unlink($convertedTmpPath);
}
} else {
http_response_code(500);
// 确保JSON输出正确,对路径进行处理以避免JSON格式问题
$debugInfo = [
'target_path' => str_replace('\\', '/', $targetPath),
'backup_target_path' => str_replace('\\', '/', $backupTargetPath),
'directory_exists' => file_exists($uploadDir),
'directory_writable' => is_writable($uploadDir),
'script_directory_writable' => is_writable($scriptDir),
'error_message' => error_get_last() ? error_get_last()['message'] : '无错误信息'
];
$response = [
'success' => false,
'message' => '无法保存上传的文件,请检查目录权限。路径:' . $targetPath,
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
}
} catch (Exception $e) {
// 全局异常处理
http_response_code(500);
$response = [
'success' => false,
'message' => '服务器处理错误:' . $e->getMessage(),
'image_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
// 确保脚本结束
exit;
?>
php 文件接口源码
[PHP] 纯文本查看 复制代码 <?php
// 设置响应头为JSON格式
header("Content-Type: application/json; charset=utf-8");
// 允许跨域请求
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-API-TIMESTAMP, X-API-SIGNATURE");
// 安全配置 - 请在实际使用时修改为强密钥
$apiKey = 'QQ516221198_QUN1071098978'; // 替换为你的API密钥
$allowedTimeWindow = 30; // 允许的请求时间窗口(秒),防止重放攻击
// 验证函数 - 修改为支持任何文件类型
function validateRequest() {
try {
global $apiKey, $allowedTimeWindow;
// 获取请求头中的时间戳和签名
$timestamp = isset($_SERVER['HTTP_X_API_TIMESTAMP']) ? $_SERVER['HTTP_X_API_TIMESTAMP'] : '';
$signature = isset($_SERVER['HTTP_X_API_SIGNATURE']) ? $_SERVER['HTTP_X_API_SIGNATURE'] : '';
// 检查必要的头信息
if (empty($timestamp) || empty($signature)) {
http_response_code(401);
$response = [
'success' => false,
'message' => '缺少必要的信息:时间戳或签名',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 验证时间戳,防止重放攻击
$currentTime = time();
if (abs($currentTime - $timestamp) > $allowedTimeWindow) {
http_response_code(401);
$response = [
'success' => false,
'message' => '请求已过期,时间差:' . abs($currentTime - $timestamp) . '秒',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 获取上传的文件名 - 只有在有文件上传时才检查
$fileName = '';
if (isset($_FILES['file']) && isset($_FILES['file']['name'])) {
$fileName = $_FILES['file']['name'];
}
// 检查文件名是否存在 - 只有在有文件上传时才需要文件名
if (empty($fileName)) {
http_response_code(401);
$response = [
'success' => false,
'message' => '无法获取文件名或未上传文件',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 生成预期的签名:时间戳+API密钥+文件名的MD5值
$expectedSignature = md5($timestamp . $apiKey . $fileName);
// 验证签名
if ($signature !== $expectedSignature) {
http_response_code(401);
$response = [
'success' => false,
'message' => '无效的签名',
'file_url' => '',
'debug_info' => [
'timestamp' => $timestamp,
'api_key' => $apiKey,
'file_name' => $fileName,
'expected_signature' => $expectedSignature,
'received_signature' => $signature
]
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
} catch (Exception $e) {
http_response_code(500);
$response = [
'success' => false,
'message' => '验证过程中发生错误:' . $e->getMessage(),
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
}
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'Preflight request successful', 'file_url' => ''], JSON_UNESCAPED_SLASHES);
exit;
}
// 处理GET请求,用于断点续传检查
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
try {
// 获取文件标识符
$fileId = isset($_GET['file_id']) ? $_GET['file_id'] : '';
if (empty($fileId)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '缺少文件ID', 'file_url' => ''], JSON_UNESCAPED_SLASHES);
exit;
}
// 检查临时文件是否存在
$tempDir = 'temp_uploads';
if (!file_exists($tempDir)) {
mkdir($tempDir, 0777, true);
@chmod($tempDir, 0777);
}
$tempFilePath = $tempDir . DIRECTORY_SEPARATOR . $fileId . '.tmp';
if (file_exists($tempFilePath)) {
// 返回已上传的大小
$uploadedSize = filesize($tempFilePath);
echo json_encode([
'success' => true,
'message' => '文件存在,支持断点续传',
'uploaded_size' => $uploadedSize,
'file_url' => ''
], JSON_UNESCAPED_SLASHES);
} else {
echo json_encode([
'success' => true,
'message' => '文件不存在,将创建新文件',
'uploaded_size' => 0,
'file_url' => ''
], JSON_UNESCAPED_SLASHES);
}
exit;
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => '检查文件状态错误:' . $e->getMessage(),
'file_url' => ''
], JSON_UNESCAPED_SLASHES);
exit;
}
}
// 处理非POST且非GET请求
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => '不支持的请求方法', 'file_url' => ''], JSON_UNESCAPED_SLASHES);
exit;
}
// 文件配置
$uploadDir = 'uploads'; // 上传目录
$tempDir = 'temp_uploads'; // 临时目录,用于断点续传
$maxFileSize = 100 * 1024 * 1024; // 最大文件大小:100MB,可根据需要调整
// 确保上传目录存在,如果不存在则创建
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
@chmod($uploadDir, 0777);
}
// 确保临时目录存在,如果不存在则创建
if (!file_exists($tempDir)) {
mkdir($tempDir, 0777, true);
@chmod($tempDir, 0777);
}
// 确保目录路径格式正确
$uploadDir = rtrim($uploadDir, '/\\') . DIRECTORY_SEPARATOR;
$tempDir = rtrim($tempDir, '/\\') . DIRECTORY_SEPARATOR;
// 获取当前日期,用于创建日期目录
$currentDate = date('Y-m-d');
$dateDir = $uploadDir . $currentDate . DIRECTORY_SEPARATOR;
// 确保日期目录存在,如果不存在则创建
if (!file_exists($dateDir)) {
mkdir($dateDir, 0777, true);
@chmod($dateDir, 0777);
}
// 获取当前API的基础URL(用于生成文件URL)
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http";
$host = $_SERVER['HTTP_HOST'];
$port = $_SERVER['SERVER_PORT'];
// 标准端口不需要重复添加
$standardPorts = ['80', '443'];
$portSuffix = in_array($port, $standardPorts) ? '' : ":$port";
// 确保host不包含端口号,避免重复
if (strpos($host, ':') !== false) {
$host = explode(':', $host)[0];
}
$baseUrl = "$protocol://$host$portSuffix{$_SERVER['REQUEST_URI']}";
$baseUrl = dirname($baseUrl) . '/';
try {
// 检查是否收到文件
if (!isset($_FILES['file'])) {
http_response_code(400);
$response = [
'success' => false,
'message' => '没有收到上传的文件',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 验证请求签名
validateRequest();
$file = $_FILES['file'];
// 检查文件上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
$response = [
'success' => false,
'message' => '文件上传错误,错误代码: ' . $file['error'],
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 获取文件唯一标识符
$fileId = isset($_POST['file_id']) ? $_POST['file_id'] : '';
if (empty($fileId)) {
// 如果没有提供file_id,则生成一个
$fileId = md5($file['name'] . time() . rand());
}
// 获取文件名和扩展名
$originalExtension = pathinfo($file['name'], PATHINFO_EXTENSION);
$extension = !empty($originalExtension) ? strtolower($originalExtension) : '';
$baseName = pathinfo($file['name'], PATHINFO_FILENAME);
// 构建临时文件路径
$tempFilePath = $tempDir . $fileId . '.tmp';
// 检查是否是断点续传请求
$isResumeUpload = isset($_SERVER['HTTP_CONTENT_RANGE']) ? true : false;
if ($isResumeUpload) {
// 解析Content-Range头,格式如: bytes 0-1023/2048
$contentRange = $_SERVER['HTTP_CONTENT_RANGE'];
if (preg_match('/bytes (\d+)-(\d+)\/(\d+)/', $contentRange, $matches)) {
$startByte = intval($matches[1]);
$endByte = intval($matches[2]);
$totalSize = intval($matches[3]);
// 确保上传的文件大小匹配Content-Range
$uploadedSize = $endByte - $startByte + 1;
if ($uploadedSize != $file['size']) {
http_response_code(400);
$response = [
'success' => false,
'message' => '上传的文件大小与Content-Range不匹配',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 检查总文件大小是否超过限制
if ($totalSize > $maxFileSize) {
http_response_code(400);
$response = [
'success' => false,
'message' => '文件过大,最大允许' . ($maxFileSize / 1024 / 1024) . 'MB',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 如果是第一次上传,创建临时文件
if (!file_exists($tempFilePath)) {
// 预分配空间
$handle = fopen($tempFilePath, 'w');
ftruncate($handle, $totalSize);
fclose($handle);
}
// 将块写入临时文件的相应位置
$handle = fopen($tempFilePath, 'r+');
fseek($handle, $startByte);
$written = fwrite($handle, file_get_contents($file['tmp_name']));
fclose($handle);
if ($written != $uploadedSize) {
http_response_code(500);
$response = [
'success' => false,
'message' => '写入文件块失败',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 检查是否上传完成
$currentSize = filesize($tempFilePath);
if ($currentSize >= $totalSize) {
// 上传完成,将临时文件移动到目标位置
$filename = $baseName . '_' . bin2hex(random_bytes(8)) . '.' . $extension;
$targetPath = $dateDir . $filename;
// 作为最后的备选方案,尝试使用当前脚本所在目录
$scriptDir = __DIR__ . DIRECTORY_SEPARATOR;
$backupTargetPath = $scriptDir . 'uploads_' . basename($targetPath);
// 移动上传的文件并设置权限
if (rename($tempFilePath, $targetPath)) {
// 设置文件权限
@chmod($targetPath, 0644);
// 返回成功信息和文件URL
$webPath = 'uploads/' . $currentDate . '/' . $filename;
$fullFileUrl = $baseUrl . $webPath;
$response = [
'success' => true,
'message' => '文件上传成功(断点续传)',
'file_url' => $fullFileUrl
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} else {
// 尝试备选路径
$backupDateDir = $scriptDir . 'uploads_' . $currentDate . DIRECTORY_SEPARATOR;
if (!file_exists($backupDateDir)) {
mkdir($backupDateDir, 0777, true);
@chmod($backupDateDir, 0777);
}
$newBackupTargetPath = $backupDateDir . basename($backupTargetPath);
// 移动到日期子目录
if (rename($tempFilePath, $newBackupTargetPath)) {
@chmod($newBackupTargetPath, 0644);
// 构建完整的文件URL
$fullFileUrl = $baseUrl . 'uploads_' . $currentDate . '/' . basename($backupTargetPath);
$response = [
'success' => true,
'message' => '文件上传成功(断点续传,使用备选路径)',
'file_url' => $fullFileUrl
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} else {
http_response_code(500);
$response = [
'success' => false,
'message' => '无法保存上传的文件,请检查目录权限。路径:' . $targetPath,
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
}
} else {
// 上传未完成,返回已上传的大小(此时不返回file_url)
$response = [
'success' => true,
'message' => '文件块上传成功',
'uploaded_size' => $currentSize,
'total_size' => $totalSize,
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
} else {
http_response_code(400);
$response = [
'success' => false,
'message' => '无效的Content-Range格式',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
} else {
// 普通上传(非断点续传)
// 检查文件大小
if ($file['size'] > $maxFileSize) {
http_response_code(400);
$response = [
'success' => false,
'message' => '文件过大,最大允许' . ($maxFileSize / 1024 / 1024) . 'MB',
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
exit;
}
// 生成唯一的文件名
$filename = $baseName . '_' . bin2hex(random_bytes(8)) . '.' . $extension;
// 构建目标路径,使用日期目录
$targetPath = $dateDir . $filename;
// 作为最后的备选方案,尝试使用当前脚本所在目录
$scriptDir = __DIR__ . DIRECTORY_SEPARATOR;
$backupTargetPath = $scriptDir . 'uploads_' . basename($targetPath);
// 移动上传的文件并设置权限
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
// 设置文件权限
@chmod($targetPath, 0644);
// 返回成功信息和文件URL
$webPath = 'uploads/' . $currentDate . '/' . $filename;
$fullFileUrl = $baseUrl . $webPath;
$response = [
'success' => true,
'message' => '文件上传成功',
'file_url' => $fullFileUrl
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} else {
// 尝试备选路径
if (move_uploaded_file($file['tmp_name'], $backupTargetPath)) {
@chmod($backupTargetPath, 0644);
// 备选路径也使用日期目录结构
$backupDateDir = $scriptDir . 'uploads_' . $currentDate . DIRECTORY_SEPARATOR;
if (!file_exists($backupDateDir)) {
mkdir($backupDateDir, 0777, true);
@chmod($backupDateDir, 0777);
}
$newBackupTargetPath = $backupDateDir . basename($backupTargetPath);
// 移动到日期子目录
rename($backupTargetPath, $newBackupTargetPath);
// 构建完整的文件URL
$fullFileUrl = $baseUrl . 'uploads_' . $currentDate . '/' . basename($backupTargetPath);
$response = [
'success' => true,
'message' => '文件上传成功(使用备选路径)',
'file_url' => $fullFileUrl
];
echo json_encode($response, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} else {
http_response_code(500);
$response = [
'success' => false,
'message' => '无法保存上传的文件,请检查目录权限。路径:' . $targetPath,
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
}
}
} catch (Exception $e) {
// 全局异常处理
http_response_code(500);
$response = [
'success' => false,
'message' => '服务器处理错误:' . $e->getMessage(),
'file_url' => ''
];
echo json_encode($response, JSON_UNESCAPED_SLASHES);
}
// 确保脚本结束
exit;
?>

好的,全部源码在这
|
评分
-
查看全部评分
|