PHP WebUploader后端处理

wen PHP项目 2

本文目录导读:

PHP WebUploader后端处理

  1. 基础配置与公共函数
  2. 入口文件 upload.php
  3. 前端示例代码
  4. 高级功能扩展
  5. 注意事项

这里为您提供一个完整的 PHP 后端处理 WebUploader 上传的解决方案,包含单文件、多文件上传以及分片上传的处理逻辑。

基础配置与公共函数

<?php
/**
 * WebUploader PHP 后端处理类
 */
class UploadHandler {
    private $config;
    public function __construct($config = []) {
        // 默认配置
        $this->config = array_merge([
            'upload_dir' => './uploads/',       // 上传保存目录
            'max_size' => 20 * 1024 * 1024,     // 最大文件大小(20MB)
            'allow_types' => ['jpg', 'jpeg', 'png', 'gif', 'zip', 'pdf'], // 允许的文件类型
            'is_trim' => true,                   // 是否去除扩展名中的空格
        ], $config);
        // 检查目录
        if (!file_exists($this->config['upload_dir'])) {
            @mkdir($this->config['upload_dir'], 0777, true);
        }
    }
    /**
     * 统一返回JSON格式
     */
    private function jsonResponse($data, $status = true) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'success' => $status,
            'data' => $data
        ]);
        exit;
    }
    /**
     * 获取文件扩展名
     */
    private function getExtension($filename) {
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        return strtolower($ext);
    }
    /**
     * 检查文件类型
     */
    private function checkType($ext) {
        if ($this->config['is_trim']) {
            $ext = trim($ext);
        }
        return in_array($ext, $this->config['allow_types']);
    }
    /**
     * 生成唯一文件名
     */
    private function generateUniqueName($ext) {
        $name = date('YmdHis') . '_' . uniqid() . '_' . rand(1000, 9999);
        return $name . '.' . $ext;
    }
    /**
     * 创建日期目录
     */
    private function createDateDir() {
        $year = date('Y');
        $month = date('m');
        $dir = $this->config['upload_dir'] . $year . '/' . $month . '/';
        if (!file_exists($dir)) {
            @mkdir($dir, 0777, true);
        }
        return $dir;
    }
    /**
     * 处理上传的主方法
     */
    public function handleUpload() {
        $action = isset($_POST['action']) ? $_POST['action'] : 'upload';
        switch ($action) {
            case 'upload':      // 普通上传
                $result = $this->processUpload();
                break;
            case 'chunk':       // 分片上传
                $result = $this->processChunkUpload();
                break;
            default:
                $this->jsonResponse(['msg' => '未知操作'], false);
        }
        $this->jsonResponse($result);
    }
    /**
     * 普通文件上传
     */
    private function processUpload() {
        if (!isset($_FILES['file'])) {
            return ['msg' => '没有文件上传'];
        }
        $file = $_FILES['file'];
        // 检查错误码
        if ($file['error'] !== UPLOAD_ERR_OK) {
            $errorMsg = $this->uploadErrorMsg($file['error']);
            return ['msg' => $errorMsg];
        }
        // 检查文件大小
        if ($file['size'] > $this->config['max_size']) {
            return ['msg' => '文件太大,最大支持 ' . ($this->config['max_size'] / 1024 / 1024) . 'MB'];
        }
        // 检查类型
        $ext = $this->getExtension($file['name']);
        if (!$this->checkType($ext)) {
            return ['msg' => '不支持的文件类型: ' . $ext];
        }
        // 生成文件名和路径
        $uploadDir = $this->createDateDir();
        $fileName = $this->generateUniqueName($ext);
        $filePath = $uploadDir . $fileName;
        // 移动文件
        if (!move_uploaded_file($file['tmp_name'], $filePath)) {
            return ['msg' => '文件保存失败'];
        }
        // 返回文件信息
        return [
            'originalName' => $file['name'],
            'fileName' => $fileName,
            'filePath' => $filePath,
            'fileSize' => $file['size'],
            'fileType' => $ext,
            'uploadTime' => date('Y-m-d H:i:s')
        ];
    }
    /**
     * 分片上传处理
     */
    private function processChunkUpload() {
        // 分片信息
        $chunk = isset($_POST['chunk']) ? intval($_POST['chunk']) : 0;       // 当前第几分片
        $chunks = isset($_POST['chunks']) ? intval($_POST['chunks']) : 1;    // 总分片数
        // 文件信息
        $originalName = isset($_POST['name']) ? $_POST['name'] : 'unknown';
        $file = isset($_FILES['file']) ? $_FILES['file'] : null;
        if (!$file) {
            return ['msg' => '没有文件数据'];
        }
        // 检查文件错误
        if ($file['error'] !== UPLOAD_ERR_OK) {
            return ['msg' => '分片上传失败'];
        }
        // 检查总大小(可选,需要前端提供)
        $totalSize = isset($_POST['size']) ? intval($_POST['size']) : 0;
        if ($totalSize > $this->config['max_size']) {
            return ['msg' => '文件太大'];
        }
        // 临时分片保存目录
        $tempDir = $this->config['upload_dir'] . 'temp/';
        if (!file_exists($tempDir)) {
            @mkdir($tempDir, 0777, true);
        }
        // 生成临时文件夹名(基于文件名和hash,这里简单处理)
        $tempFolder = $tempDir . md5($originalName . $totalSize);
        if (!file_exists($tempFolder)) {
            @mkdir($tempFolder, 0777, true);
        }
        // 保存分片
        $chunkFile = $tempFolder . '/' . $chunk;
        if (!move_uploaded_file($file['tmp_name'], $chunkFile)) {
            return ['msg' => '分片保存失败'];
        }
        // 检查是否所有分片都已上传
        if ($this->isUploadComplete($tempFolder, $chunks)) {
            // 合并分片
            $result = $this->mergeChunks($tempFolder, $chunks, $originalName);
            return $result;
        }
        return [
            'chunk' => $chunk,
            'chunks' => $chunks,
            'msg' => '分片上传成功'
        ];
    }
    /**
     * 检查分片是否全部上传完成
     */
    private function isUploadComplete($tempFolder, $chunks) {
        for ($i = 0; $i < $chunks; $i++) {
            if (!file_exists($tempFolder . '/' . $i)) {
                return false;
            }
        }
        return true;
    }
    /**
     * 合并分片
     */
    private function mergeChunks($tempFolder, $chunks, $originalName) {
        // 获取文件扩展名
        $ext = $this->getExtension($originalName);
        if (!$this->checkType($ext)) {
            return ['msg' => '不支持的文件类型: ' . $ext];
        }
        // 创建最终上传目录
        $uploadDir = $this->createDateDir();
        $fileName = $this->generateUniqueName($ext);
        $filePath = $uploadDir . $fileName;
        // 写入文件
        $file = fopen($filePath, 'wb');
        if (!$file) {
            return ['msg' => '无法创建最终文件'];
        }
        // 按顺序合并分片
        for ($i = 0; $i < $chunks; $i++) {
            $chunkFile = $tempFolder . '/' . $i;
            if (file_exists($chunkFile)) {
                $content = file_get_contents($chunkFile);
                fwrite($file, $content);
                @unlink($chunkFile); // 删除分片
            }
        }
        fclose($file);
        // 删除临时目录
        @rmdir($tempFolder);
        // 获取文件大小
        $fileSize = filesize($filePath);
        return [
            'originalName' => $originalName,
            'fileName' => $fileName,
            'filePath' => $filePath,
            'fileSize' => $fileSize,
            'fileType' => $ext,
            'uploadTime' => date('Y-m-d H:i:s'),
            'msg' => '上传成功'
        ];
    }
    /**
     * 获取上传错误信息
     */
    private function uploadErrorMsg($errorCode) {
        $messages = [
            UPLOAD_ERR_INI_SIZE => '文件大小超过服务器限制',
            UPLOAD_ERR_FORM_SIZE => '文件大小超过表单限制',
            UPLOAD_ERR_PARTIAL => '文件只有部分被上传',
            UPLOAD_ERR_NO_FILE => '没有文件被上传',
            UPLOAD_ERR_NO_TMP_DIR => '找不到临时文件夹',
            UPLOAD_ERR_CANT_WRITE => '文件写入失败',
            UPLOAD_ERR_EXTENSION => '文件上传被扩展阻止',
        ];
        return isset($messages[$errorCode]) ? $messages[$errorCode] : '未知错误';
    }
}

入口文件 upload.php

<?php
/**
 * WebUploader 上传入口文件
 * 使用方法: 引入此文件即可
 */
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 配置
$config = [
    'upload_dir' => __DIR__ . '/uploads/',   // 上传目录
    'max_size' => 100 * 1024 * 1024,         // 最大100MB
    'allow_types' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'zip', 'rar', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'mp4', 'mp3'], 
];
// 实例化并处理
$handler = new UploadHandler($config);
$handler->handleUpload();
?>

前端示例代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">WebUploader 文件上传</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.5/webuploader.min.css">
    <style>
        .uploader-list {
            margin: 20px 0;
        }
        .file-item {
            padding: 10px;
            border: 1px solid #ddd;
            margin: 5px 0;
            background: #f9f9f9;
        }
        .progress {
            height: 10px;
            background: #e0e0e0;
            margin-top: 5px;
        }
        .progress-bar {
            height: 100%;
            background: #4CAF50;
        }
        #picker {
            padding: 10px 20px;
            background: #2196F3;
            color: white;
            border: none;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="uploader">
        <div id="picker">选择文件</div>
        <div class="uploader-list" id="fileList"></div>
        <button type="button" class="btn btn-start">开始上传</button>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/webuploader/0.1.5/webuploader.min.js"></script>
    <script>
    $(function() {
        var uploader = WebUploader.create({
            // 上传地址
            server: 'upload.php',
            // 使用分片上传
            chunked: true,
            chunkSize: 2 * 1024 * 1024, // 每片2MB
            // 并发数
            threads: 3,
            // 文件类型
            accept: {
                title: 'Images',
                extensions: 'jpg,jpeg,png,gif',
                mimeTypes: 'image/jpeg,image/png,image/gif'
            },
            // 单个文件大小限制
            fileSizeLimit: 100 * 1024 * 1024,
            fileSingleSizeLimit: 50 * 1024 * 1024,
            // 选择文件按钮
            pick: '#picker'
        });
        // 添加到上传队列
        uploader.on('fileQueued', function(file) {
            var html = '<div class="file-item" id="' + file.id + '">' +
                       '<span class="file-name">' + file.name + '</span>' +
                       '<span class="file-size">' + Math.floor(file.size / 1024 / 1024) + 'MB</span>' +
                       '<div class="progress"><div class="progress-bar" style="width:0%"></div></div>' +
                       '<span class="file-status"></span>' +
                       '</div>';
            $('#fileList').append(html);
        });
        // 上传进度
        uploader.on('uploadProgress', function(file, percentage) {
            var $li = $('#' + file.id);
            $li.find('.progress-bar').css('width', percentage * 100 + '%');
        });
        // 上传成功
        uploader.on('uploadSuccess', function(file, response) {
            var $li = $('#' + file.id);
            $li.find('.file-status').text('上传成功: ' + response.data.filePath);
        });
        // 上传失败
        uploader.on('uploadError', function(file, reason) {
            var $li = $('#' + file.id);
            $li.find('.file-status').text('上传失败: ' + reason);
        });
        // 全部上传完成
        uploader.on('uploadFinished', function() {
            alert('所有文件上传完成');
        });
        // 开始上传按钮
        $('.btn-start').on('click', function() {
            uploader.upload();
        });
    });
    </script>
</body>
</html>

高级功能扩展

<?php
/**
 * 扩展功能:包括图片压缩、下载、进度查询等
 */
class AdvancedUploadHandler extends UploadHandler {
    /**
     * 图片压缩处理
     */
    public function compressImage($sourcePath, $targetPath, $maxWidth = 1000) {
        // 获取图片信息
        list($width, $height, $type) = getimagesize($sourcePath);
        // 如果图片宽度小于最大宽度,不进行压缩
        if ($width <= $maxWidth) {
            copy($sourcePath, $targetPath);
            return true;
        }
        // 计算压缩后尺寸
        $ratio = $maxWidth / $width;
        $newWidth = $maxWidth;
        $newHeight = $height * $ratio;
        // 创建画布
        $thumb = imagecreatetruecolor($newWidth, $newHeight);
        // 根据类型加载源图
        switch ($type) {
            case IMAGETYPE_JPEG:
                $source = imagecreatefromjpeg($sourcePath);
                imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
                imagejpeg($thumb, $targetPath, 80);
                break;
            case IMAGETYPE_PNG:
                $source = imagecreatefrompng($sourcePath);
                imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
                imagepng($thumb, $targetPath, 6);
                break;
            default:
                return false;
        }
        imagedestroy($thumb);
        imagedestroy($source);
        return true;
    }
    /**
     * 异步下载大文件
     */
    public function downloadFile($filePath, $downloadName = '') {
        if (!file_exists($filePath)) {
            http_response_code(404);
            exit('File not found');
        }
        if (empty($downloadName)) {
            $downloadName = basename($filePath);
        }
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $downloadName . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($filePath));
        readfile($filePath);
        exit;
    }
    /**
     * 获取上传进度(需要配合前端使用)
     */
    public function getProgress($tempDir) {
        $files = glob($tempDir . '/*');
        if ($files) {
            return count($files);
        }
        return 0;
    }
}
?>

注意事项

1 安全配置

  • 设置 Web 服务器上传大小限制(PHP.ini)
  • 验证文件类型(不仅靠扩展名,还要检查 MIME)
  • 使用安全的文件命名方式
  • 控制上传目录的访问权限

2 PHP 配置建议

; php.ini 配置
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
memory_limit = 128M

3 前端注意事项

  • 合理设置分片大小(2MB-5MB为宜)
  • 设置并发数(2-3个)
  • 添加超时处理
  • 实现断点续传逻辑

这个方案完整支持了 WebUploader 的普通上传和分片上传功能,可以根据实际需求调整配置和扩展功能。

抱歉,评论功能暂时关闭!