开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 1761|回复: 135
收起左侧

[2025开源大赛(第八届)] PHP支持✅文件上传✅主流图片上传✅全开源 带验证 简单实用

[复制链接]
结帖率:100% (11/11)
发表于 3 天前 | 显示全部楼层 |阅读模式   广东省茂名市
分享源码
界面截图:
是否带模块: 纯源码
备注说明: -


一、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”#换行符#换行符 )读入文件 (图片地址)到字节集 ( #换行符“--” + 分隔符 + “--”)
' 发送POST请求
上传结果 = 编码_编码转换对象 (网页_访问_对象 (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”#换行符#换行符 )读入文件 (图片地址)到字节集 ( #换行符“--” + 分隔符 + “--”)
' 发送POST请求
上传结果 = 编码_编码转换对象 (网页_访问_对象 (api地址, 1, , , , 头信息, , , , 表单数据, , , , , , , , , ), , , )
json.解析 (到文本 (上传结果), , )
调试输出 (到文本 (上传结果))
如果 (json.取属性对象 (“success”)“true”)
图片地址 = json.取通用属性 (“['file_url']”, )
返回 ()
图片地址 = “”
错误提示 = json.取通用属性 (“message”, )
返回 ()


i支持库列表   支持库注释   
spec特殊功能支持库


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



好的,全部源码在这
游客,如果您要查看本帖隐藏内容请回复
我们一起为广大易友做贡献!

评分

参与人数 3精币 +3 收起 理由
文西哥 + 1 感谢分享,很给力!~
cui870222829 + 1 感谢分享,很给力!~
远古石桥 + 1 YYDS~!

查看全部评分


发表于 12 分钟前 | 显示全部楼层   山东省潍坊市
yyyyyyyyyyyyyyy
回复 支持 反对

使用道具 举报

签到天数: 2 天

发表于 7 小时前 | 显示全部楼层   河北省衡水市
点了下载扣了4分..然后就没有然后了

点评

啥情况?加群下载   广东省茂名市  发表于 7 小时前
回复 支持 反对

使用道具 举报

签到天数: 2 天

发表于 7 小时前 | 显示全部楼层   河北省衡水市
如果您要查看本帖隐藏内容请回复
回复 支持 反对

使用道具 举报

签到天数: 19 天

发表于 昨天 23:30 | 显示全部楼层   广东省茂名市
很不错的例子~
回复 支持 反对

使用道具 举报

结帖率:83% (5/6)

签到天数: 17 天

发表于 昨天 22:38 | 显示全部楼层   江苏省常州市
支持!!!!!!!!!
回复 支持 反对

使用道具 举报

签到天数: 13 天

发表于 昨天 20:43 | 显示全部楼层   广东省梅州市
收藏了,感谢分享
回复 支持 反对

使用道具 举报

结帖率:32% (7/22)
发表于 昨天 19:32 | 显示全部楼层   浙江省嘉兴市
收藏了,感谢分享
回复 支持 反对

使用道具 举报

结帖率:100% (4/4)

签到天数: 20 天

发表于 昨天 15:05 | 显示全部楼层   上海市上海市

回帖奖励 +2 枚 精币

回复 支持 反对

使用道具 举报

签到天数: 15 天

发表于 昨天 13:31 | 显示全部楼层   广东省广州市

回帖奖励 +2 枚 精币

支持开源~!感谢分享
回复 支持 反对

使用道具 举报

结帖率:28% (5/18)

签到天数: 1 天

发表于 昨天 13:30 | 显示全部楼层   福建省泉州市
文件上传
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

关闭

精易论坛 - 有你更精彩上一条 /2 下一条

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备2025452707号) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表