文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

大文件分片上传的实现【前后台完整版】

2023-08-31 20:43

关注

在一般的产品开发过程中,大家多少会遇到上传视频功能的需求,往往我们采用的都是对视频大小进行限制等方法,来防止上传请求超时,导致上传失败。这时候可能将视频分片上传可以对你的项目有一个小小的体验优化。

本片文章前端是vue,后台基于PHP进行的分片上传,需要的小伙伴可以借鉴。

分片上传

什么是分片上传

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。

分片上传的场景

(1)大文件上传

(2)网络环境环境不好,存在需要重传风险的场景

实现流程步骤

a、方案一,常规步骤、本文实现的步骤

将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;

初始化一个分片上传任务,返回本次分片上传唯一标识;

按照一定的策略(串行或并行)发送各个分片数据块;

发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件。

b、方案二

前端(客户端)需要根据固定大小对文件进行分片,请求后端(服务端)时要带上分片序号和大小

服务端创建conf文件用来记录分块位置,conf文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认的0,已上传的就是Byte.MAX_VALUE 127(这步是实现断点续传和秒传的核心步骤)

服务器按照请求数据中给的分片序号和每片分块大小(分片大小是固定且一样的)算出开始位置,与读取到的文件片段数据,写入文件。

前端代码

template

// 上传按钮样式

移入方法

import { uploadByPieces } from "@/utils/upload"; //引入uploadByPieces方法methods// 分片上传videoSaveToUrl(file) {  uploadByPieces({    file: file, // 获取到的视频文件    pieceSize: 3, // 分片大小  这里是3M一片    success: (data) => {      this.formValidate.video_link = data.file_path;      this.progress = 100;    // 上传成功 进度条为100%    },    error: (e) => {      this.$Message.error(e.msg);  //报错信息    },    uploading: (chunk, allChunk) => {      this.videoIng = true;   // 上传时进度条展示 根据需要添加      let st = Math.floor((chunk / allChunk) * 100);  这里是用上传的第几片除以总片数进行百分比计算      this.progress = st;    },  });  return false;},utils/upload

utils/upload

import md5 from 'js-md5' //引入MD5加密import { upload } from '@/api/upload.js'  // 这里指前端调用接口的api方法export const uploadByPieces = ({ file, pieceSize = 2, success, error, uploading }) => {    // 如果文件传入为空直接 return 返回    if (!file) return    let fileMD5 = ''// 总文件列表    const chunkSize = pieceSize * 1024 * 1024 // 5MB一片    const chunkCount = Math.ceil(file.size / chunkSize) // 总片数    console.log(chunkSize, chunkCount)    // 获取md5    const readFileMD5 = () => {        // 读取视频文件的md5        console.log("获取文件的MD5值")        let fileRederInstance = new FileReader()        console.log('file', file)        fileRederInstance.readAsBinaryString(file)        fileRederInstance.addEventListener('load', e => {            let fileBolb = e.target.result            fileMD5 = md5(fileBolb)            console.log('fileMD5', fileMD5)            console.log("文件未被上传,将分片上传")            readChunkMD5()        })    }    const getChunkInfo = (file, currentChunk, chunkSize) => {        let start = currentChunk * chunkSize        let end = Math.min(file.size, start + chunkSize)        let chunk = file.slice(start, end)        return { start, end, chunk }    }    // 针对每个文件进行chunk处理    const readChunkMD5 = async () => {        // 针对单个文件进行chunk上传        for (var i = 0; i < chunkCount; i++) {            const { chunk } = getChunkInfo(file, i, chunkSize)            console.log("总片数" + chunkCount)            console.log("分片后的数据---测试:" + i)            await uploadChunk({ chunk, currentChunk: i, chunkCount })        }    }    const uploadChunk = (chunkInfo) => {        // progressFun()        return new Promise((resolver, reject) => {            let config = {                headers: {                    'Content-Type': 'multipart/form-data'                }            }            // 创建formData对象,下面是结合不同项目给后端传入的对象。            let fetchForm = new FormData()            fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1)  // 第几片            fetchForm.append('chunkSize', chunkSize)  // 分片大小的限制  例如限制 5M            fetchForm.append('currentChunkSize', chunkInfo.chunk.size)  // 每一片的大小            fetchForm.append('file', chunkInfo.chunk)   //每一片的文件            fetchForm.append('filename', file.name)  // 文件名             fetchForm.append('totalChunks', chunkInfo.chunkCount) //总片数            fetchForm.append('md5', fileMD5)            upload(fetchForm, config).then(res => {                console.log("分片上传返回信息:", res)                if (res.data.code == 1) {                    // // 结合不同项目 将成功的信息返回出去                    // 下面如果在项目中没有用到可以不用打开注释                    uploading(chunkInfo.currentChunk + 1, chunkInfo.chunkCount)                    resolver(true)                } else if (res.data.code == 2) {                    if (chunkInfo.currentChunk < chunkInfo.chunkCount - 1) {                        console.log("分片上传成功")                    } else {                        // 当总数大于等于分片个数的时候                        if ((chunkInfo.currentChunk + 1) == chunkInfo.chunkCount) {console.log("文件开始------合并成功")success(res.data)                        }                    }                }            }).catch((e) => {                error && error(e)            })        })    }    readFileMD5() // 开始执行代码}

后端代码

控制器

    public function videoUpload()    {        $data = $this->request->postMore([            ['chunkNumber', 0],//第几分片            ['currentChunkSize', 0],//分片大小            ['chunkSize', 0],//总大小            ['totalChunks', 0],//分片总数            ['file', 'file'],//文件            ['md5', ''],//MD5            ['filename', ''],//文件名称        ]);        $res = $this->service->videoUpload($data, $_FILES['file']);        return app('json')->success($res);    }

方法

    public function videoUpload($data, $file)    {        $public_dir = app()->getRootPath() . 'public';        $dir = '/uploads/attach/' . date('Y') . DIRECTORY_SEPARATOR . date('m') . DIRECTORY_SEPARATOR . date('d');        $all_dir = $public_dir . $dir;        if (!is_dir($all_dir)) mkdir($all_dir, 0777, true);        $filename = $all_dir . '/' . $data['filename'] . '__' . $data['chunkNumber'];        move_uploaded_file($file['tmp_name'], $filename);        $res['code'] = 0;        $res['msg'] = 'error';        $res['file_path'] = '';        if ($data['chunkNumber'] == $data['totalChunks']) {            $blob = '';            for ($i = 1; $i <= $data['totalChunks']; $i++) {                $blob .= file_get_contents($all_dir . '/' . $data['filename'] . '__' . $i);            }            file_put_contents($all_dir . '/' . $data['filename'], $blob);            for ($i = 1; $i <= $data['totalChunks']; $i++) {                @unlink($all_dir . '/' . $data['filename'] . '__' . $i);            }            if (file_exists($all_dir . '/' . $data['filename'])) {                $res['code'] = 2;                $res['msg'] = 'success';                $res['file_path'] = $dir . '/' . $data['filename'];            }        } else {            if (file_exists($all_dir . '/' . $data['filename'] . '__' . $data['chunkNumber'])) {                $res['code'] = 1;                $res['msg'] = 'waiting';                $res['file_path'] = '';            }        }        return $res;    }

在实现分片上传的过程,需要前端和后端配合,比如前后端的上传块号的文件大小,前后端必须得要一致,否则上传就会有问题。其次文件相关操作正常都是要搭建一个文件服务器的,比如使用fastdfs、hdfs等。

本示例代码在电脑配置为4核内存8G情况下,上传24G大小的文件,上传时间需要30多分钟,主要时间耗费在前端的md5值计算,后端写入的速度还是比较快。

如果项目组觉得自建文件服务器太花费时间,且项目的需求仅仅只是上传下载,那么推荐使用阿里的oss服务器,其介绍可以查看官网:

https://help.aliyun.com/product/31815.html

阿里的oss它本质是一个对象存储服务器,而非文件服务器,因此如果有涉及到大量删除或者修改文件的需求,oss可能就不是一个好的选择。

以上就是视频分片上传的前后台的所有代码,其中有需求小伙伴可以自行加入视频上传验证,断点续传等操作。

来源地址:https://blog.csdn.net/weixin_64051447/article/details/129853820

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯