导读:本期聚焦于小伙伴创作的《PHP实现大文件分片上传与进度显示:前端结合后端的完整解决方案》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《PHP实现大文件分片上传与进度显示:前端结合后端的完整解决方案》有用,将其分享出去将是对创作者最好的鼓励。

// 假设通过POST接收合并请求
if ($_POST['action'] == 'merge') {
    $file_md5 = $_POST['file_md5'];
    $filename = $_POST['filename']; // 原始文件名
    $temp_dir = 'uploads/temp/' . $file_md5 . '/';
    $final_path = 'uploads/final/' . $filename;

    // 检查临时目录是否存在
    if (!is_dir($temp_dir)) {
        http_response_code(404);
        echo json_encode(['error' => 'Temporary directory not found']);
        exit;
    }

    // 获取目录下所有分片文件并按索引排序
    $chunk_files = glob($temp_dir . '*');
    sort($chunk_files, SORT_NUMERIC);

    // 打开最终文件进行写入
    $final_handle = fopen($final_path, 'wb');
    if ($final_handle === false) {
        http_response_code(500);
        echo json_encode(['error' => 'Cannot create final file']);
        exit;
    }

    $merge_success = true;
    foreach ($chunk_files as $chunk_file) {
        $chunk_data = file_get_contents($chunk_file);
        if ($chunk_data === false || fwrite($final_handle, $chunk_data) === false) {
            $merge_success = false;
            break;
        }
        // 可选:删除已合并的分片以释放空间
        unlink($chunk_file);
    }
    fclose($final_handle);

    // 删除临时目录
    rmdir($temp_dir);

    if ($merge_success) {
        echo json_encode([
            'success' => true,
            'message' => 'File merged successfully.',
            'url' => $final_path
        ]);
    } else {
        // 如果合并失败,清理可能已部分写入的最终文件
        if (file_exists($final_path)) {
            unlink($final_path);
        }
        http_response_code(500);
        echo json_encode(['error' => 'File merge failed']);
    }
}

四、前端进度显示实现

前端需要计算并展示整体进度。整体进度 = (已成功上传的分片数 / 总分片数) * 100%。

<!DOCTYPE html>
<html>
<head>
    <title>视频上传进度显示</title>
</head>
<body>
    <input type="file" id="videoFile" accept="video/*">
    <button onclick="startUpload()">开始上传</button>
    <br><br>
    <div>上传进度: <span id="progressText">0%</span></div>
    <progress id="progressBar" value="0" max="100"></progress>

    <script>
        const CHUNK_SIZE = 2 * 1024 * 1024; // 每个分片2MB
        let totalChunks = 0;
        let uploadedChunks = 0;
        const uploadUrl = 'https://www.ipipp.com/upload.php'; // 上传接口地址

        async function startUpload() {
            const fileInput = document.getElementById('videoFile');
            if (!fileInput.files.length) {
                alert('请先选择一个视频文件');
                return;
            }
            const file = fileInput.files[0];
            totalChunks = Math.ceil(file.size / CHUNK_SIZE);
            uploadedChunks = 0;
            updateProgress(); // 初始化进度显示

            const fileMd5 = await calculateFileMD5(file); // 假设此函数能计算文件MD5

            const chunks = [];
            for (let i = 0; i < totalChunks; i++) {
                const start = i * CHUNK_SIZE;
                const end = Math.min(start + CHUNK_SIZE, file.size);
                chunks.push({
                    index: i,
                    blob: file.slice(start, end)
                });
            }

            // 控制并发数,例如最多同时上传3个分片
            const MAX_CONCURRENT = 3;
            for (let i = 0; i < chunks.length; i += MAX_CONCURRENT) {
                const chunkBatch = chunks.slice(i, i + MAX_CONCURRENT);
                await Promise.all(chunkBatch.map(chunk => uploadChunk(chunk, fileMd5, totalChunks)));
            }

            // 所有分片上传完成后,请求合并
            await mergeFile(fileMd5, file.name);
            alert('文件上传并合并完成!');
        }

        function uploadChunk(chunk, fileMd5, totalChunks) {
            return new Promise((resolve, reject) => {
                const formData = new FormData();
                formData.append('file_md5', fileMd5);
                formData.append('chunk_index', chunk.index);
                formData.append('total_chunks', totalChunks);
                formData.append('chunk_data', chunk.blob);

                const xhr = new XMLHttpRequest();
                xhr.open('POST', uploadUrl, true);

                // 监听当前分片的上传进度(可选,用于更细粒度的显示)
                xhr.upload.onprogress = function(event) {
                    if (event.lengthComputable) {
                        // 可以在这里计算更精确的进度,本例简化处理
                    }
                };

                xhr.onload = function() {
                    if (xhr.status === 200) {
                        uploadedChunks++;
                        updateProgress();
                        resolve();
                    } else {
                        reject(new Error(`Upload failed for chunk ${chunk.index}`));
                    }
                };
                xhr.onerror = function() {
                    reject(new Error('Network error'));
                };
                xhr.send(formData);
            });
        }

        async function mergeFile(fileMd5, filename) {
            const formData = new FormData();
            formData.append('action', 'merge');
            formData.append('file_md5', fileMd5);
            formData.append('filename', filename);

            const response = await fetch(uploadUrl, {
                method: 'POST',
                body: formData
            });
            const result = await response.json();
            if (!result.success) {
                throw new Error(result.error);
            }
            console.log('Merge result:', result);
        }

        function updateProgress() {
            const percent = totalChunks > 0 ? Math.round((uploadedChunks / totalChunks) * 100) : 0;
            document.getElementById('progressText').textContent = percent + '%';
            document.getElementById('progressBar').value = percent;
        }

        // 简易的文件MD5计算函数(注意:前端计算大文件MD5可能耗时,实际项目中可使用Web Crypto API或上传后由服务器计算)
        async function calculateFileMD5(file) {
            // 此处为简化示例,返回一个模拟值。实际应用应使用密码学API。
            return 'simulated_md5_hash_for_' + file.name + '_' + file.size;
        }
    </script>
</body>
</html>

五、优化与注意事项

  • 断点续传: 服务器端可以记录已上传的分片索引。在上传开始前,前端先查询服务器,跳过已上传的分片,实现断点续传功能。

  • 安全性: 务必对上传的文件进行严格的验证,包括文件类型(通过MIME类型和后缀)、大小限制,并将最终文件存储在Web根目录之外或配置正确的访问权限,防止恶意文件上传和执行。

  • 服务器配置: 确保PHP配置(php.ini)中的upload_max_filesizepost_max_size以及max_execution_time等参数足以支持大文件上传。

  • 进度准确性: 本示例以前端成功发送请求作为分片上传成功的依据。更严谨的做法是,以后端确认存储成功并返回特定响应为准。

  • 替代方案: 对于更复杂的应用,可以考虑使用专门的JavaScript库(如Resumable.js、Uppy)或云存储服务提供的SDK,它们通常内置了强大的分片上传和进度管理功能。

通过上述分片上传的方案,我们能够有效地实现视频文件上传的进度显示,大幅提升用户在处理大文件上传时的体验。开发者可以根据项目的具体需求,对前后端的逻辑进行进一步的定制和增强。

PHP分片上传 文件上传进度显示 大文件上传 断点续传 XMLHttpRequest

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。