摘要

视频文件分片上传,整体思路是利用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 目录。

作者

TANKING