插件窝 干货文章 JavaScript+PHP实现视频文件分片上传的示例代码

JavaScript+PHP实现视频文件分片上传的示例代码

分片 上传 文件 fileName 110    来源:    2024-10-18

摘要

视频文件分片上传,整体思路是利用JavaScript将文件切片,然后循环调用上传接口 upload.php 将切片上传到服务器。这样将由原来的一个大文件上传变为多个小文件同时上传,节省了上传时间,这就是文件分片上传的其中一个好处。

在这里插入图片描述

上代码

index.html

通过前端将文件对象切分成多个小块,然后依次将这些小块的文件对象上传到服务器。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>视频文件分片上传</title>
        <style>
            *{
                padding: 0;
                margin: 0;
            }
            .title {
                text-align: center;
                font-size: 25px;
                margin-top: 50px;
            }
            .video_upload {
                width: 500px;
                height: 60px;
                background: #eee;
                margin: 30px auto 0;
                border: 2px dashed #ccc;
                border-radius: 10px;
                position: relative;
                cursor: pointer;
                text-align: center;
                font-size: 25px;
                line-height: 60px;
                color: #666;
            }
            #fileInput {
                width: 100%;
                height: 100%;
                position: absolute;
                left: 0;
                top: 0;
                opacity: 0;
                cursor: pointer;
            }
            #uploadButton {
                width: 130px;
                height: 40px;
                border: none;
                outline: none;
                border-radius: 10px;
                font-size: 17px;
                margin: 10px auto;
            }
            #ret {
                text-align: center;
                font-size: 16px;
                margin-top: 20px;
            }
            #ret video {
                width: 450px;
            }
        </style>
    </head>
    <body>
        
        <p class="title">javaScript+PHP实现视频文件分片上传</p>
        <div class="video_upload">
            <span class="text"> + </span>
            <input type="file" id="fileInput" accept="video/*">
        </div>
        <button id="uploadButton" style="display:none;">开始上传</button>
        <p id="ret"></p>

        <script>
        
            // 定义全局变量
            let videoFile = null;
            let chunkSize = 1024 * 1024; // 1MB 分片大小
            
            // 当文件选择框的值改变时触发该函数
            function handleFileSelect(event) {
                const fileList = event.target.files;
                if (fileList.length > 0) {
                    videoFile = fileList[0];
                    console.log("选择了文件: ", videoFile.name);
                    document.querySelector('.video_upload .text').textContent = videoFile.name;
                    document.querySelector('#uploadButton').style.display = 'block';
                }
            }
            
            // 分片并上传文件
            async function uploadFile() {
                if (!videoFile) {
                    console.error("请选择一个视频文件");
                    return;
                }
            
                const fileSize = videoFile.size;
                let start = 0;
                let end = Math.min(chunkSize, fileSize);
                let chunkIndex = 0;
            
                // 获取文件名
                const fileName = videoFile.name;
            
                while (start < fileSize) {
                    const chunk = videoFile.slice(start, end); // 从文件中截取一个分片
            
                    // 使用FormData来构建multipart/form-data格式的请求体
                    const formData = new FormData();
                    formData.append('file', chunk);
                    formData.append('chunkIndex', chunkIndex);
                    formData.append('fileName', fileName); // 将文件名作为 formData 的一部分
            
                    try {
                        const response = await fetch('upload.php', {
                            method: 'POST',
                            body: formData
                        });
            
                        if (!response.ok) {
                            throw new Error('上传失败');
                        }
            
                        console.log('上传分片 ', chunkIndex, ' 成功');
                    } catch (error) {
                        console.error('上传分片 ', chunkIndex, ' 失败: ', error.message);
                        return;
                    }
            
                    start = end;
                    end = Math.min(start + chunkSize, fileSize);
                    chunkIndex++;
                }
            
                console.log('文件上传完成');
            
                // 上传完成后发送通知给服务器进行合并
                notifyServerForMerge(fileName);
            }
            
            // 发送通知给服务器进行合并
            async function notifyServerForMerge(fileName) {
                try {
                    const response = await fetch('merge_chunks.php', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({ fileName: fileName })
                    });
            
                    if (!response.ok) {
                        throw new Error('无法通知服务器进行合并');
                    }
                    
                    const res_data = await response.json();
            
                    console.log('已通知服务器进行合并');
                    document.querySelector('.video_upload .text').textContent = '分片合并完成!';
                    document.querySelector('#ret').innerHTML = '<video autoplay controls src="'+res_data.filePath+'"></video>';
                    document.querySelector('#uploadButton').style.display = 'none';
                } catch (error) {
                    console.error('通知服务器进行合并时发生错误: ', error.message);
                }
            }
            
            // 注册文件选择框的change事件
            document.getElementById('fileInput').addEventListener('change', handleFileSelect);
            
            // 注册上传按钮的click事件
            document.getElementById('uploadButton').addEventListener('click', uploadFile);
        </script>

    </body>
</html>

upload.php

这个是用于接收前端传过来的每一段分片,然后上传到 uploads 文件夹,上传之后就是一段一段的小分片。

<?php

    // 设置允许跨域访问
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Methods: POST");
    
    // 检查是否接收到文件和分片索引
    if (isset($_FILES['file']['error']) && isset($_POST['chunkIndex']) && isset($_POST['fileName'])) {
        
        $error = $_FILES['file']['error'];
        $chunkIndex = $_POST['chunkIndex'];
        $fileName = $_POST['fileName']; // 获取文件名
        
        // 检查是否有错误
        if ($error !== UPLOAD_ERR_OK) {
            http_response_code(500);
            echo json_encode(array(
                'error' => '文件上传失败'
            ));
            exit();
        }
        
        // 设置存储目录和文件名
        $uploadDir = './uploads/';
        $filePath = $uploadDir . $fileName . '.' . $chunkIndex;
        
        // 将分片移动到指定的目录
        if (move_uploaded_file($_FILES['file']['tmp_name'], $filePath)) {
            
            echo json_encode(array(
                'success' => '分片上传成功'
            ));
        } else {
            
            http_response_code(500);
            echo json_encode(array(
                'error' => '分片上传失败'
            ));
        }
    } else {
        
        http_response_code(400);
        echo json_encode(array(
            'error' => '缺少文件、分片索引或文件名'
        ));
    }
    
?>

merge_chunks.php

这个是用来合并分片的,当前端完成上传分片的操作,前端会异步告诉服务器你已经完成所有分片的上传,接下来将每个分片名告诉合并程序完成所有分片的合并,合并之后就是一个完整的视频文件。

<?php

    // 设置允许跨域访问
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Methods: POST");
    header("Content-Type: application/json");
    
    // 获取请求体中的文件名
    $data = json_decode(file_get_contents("php://input") , true);
    $fileName = isset($data['fileName']) ? $data['fileName'] : null;
    if ($fileName) {
        
        $uploadDir = './uploads/';
        $finalFilePath = $uploadDir . $fileName;
        $totalChunks = count(glob($uploadDir . $fileName . '.*'));
        
        // 检查是否所有分片都已上传
        if ($totalChunks > 0) {
            
            // 所有分片都已上传,开始合并
            $finalFile = fopen($finalFilePath, 'wb');
            
            // 逐个读取分片并写入最终文件
            for ($i = 0; $i < $totalChunks; $i++) {
                $chunkFilePath = $uploadDir . $fileName . '.' . $i;
                $chunkFile = fopen($chunkFilePath, 'rb');
                stream_copy_to_stream($chunkFile, $finalFile);
                fclose($chunkFile);
                unlink($chunkFilePath); // 删除已合并的分片
                
            }
            
            fclose($finalFile);
            http_response_code(200);
            echo json_encode(array(
                'success' => '文件合并成功',
                'filePath' => $finalFilePath
            ));
        } else {
            
            http_response_code(400);
            echo json_encode(array(
                'error' => '没有上传的分片'
            ));
        }
    } else {
        
        http_response_code(400);
        echo json_encode(array(
            'error' => '缺少文件名'
        ));
    }
?>

程序目录

请自行创建 uploads 目录。

在这里插入图片描述

以上就是JavaScript+PHP实现视频文件分片上传的示例代码的详细内容,更多关于JavaScript+PHP视频文件上传的资料请关注插件窝其它相关文章!