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

这篇具有很好参考价值的文章主要介绍了大文件分片上传的实现【前后台完整版】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

分片上传

1、什么是分片上传

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

2、分片上传的场景

(1)大文件上传

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

3、实现流程步骤

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() // 开始执行代码
}

后端代码

控制器

/**
     * 视频分片上传
     * @return mixed
     */
    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);
    }

方法

/**
     * 视频分片上传
     * @param $data
     * @param $file
     * @return mixed
     */
    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://www.toymoban.com/news/detail-498438.html

到了这里,关于大文件分片上传的实现【前后台完整版】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前后台协议联调&拦截器

    目标 能够完成前后台功能整合开发 掌握拦截器的编写 创建一个Web的Maven项目 pom.xml添加SSM整合所需jar包 创建对应的配置类 编写Controller、Service接口、Service实现类、Dao接口和模型类 resources下提供jdbc.properties配置文件 内容参考前面的项目或者直接使用前面的项目进行本节内容

    2023年04月14日
    浏览(45)
  • 如何判断Android应用置于前后台

            项目开发过程中总会遇到判断应用程序是否置于后台或者从后台切换到前台。往往在切换的时候应用会通过请求接口更新页面展示数据或者提示广告信息等相关操作。因此对于开发者来说判断应用程序前后台状态还是比较重要的。下面记录两种判断应用前后台状态的

    2024年02月12日
    浏览(42)
  • 关于单片机的前后台系统

    单片机裸机系统,通常又被称为前后台系统。 百度百科中,对前后台系统有一段解释: ​前后台系统,即计算机前后台系统,早期的嵌入式系统中没有操作系统的概念,程序员编写嵌入式程序通常直接面对裸机及裸设备,在这种情况下,通常把嵌入式程序分成两部分,即前

    2024年02月12日
    浏览(36)
  • android 监听app前后台切换

    需求是统计app使用时长,要求在按home键的时候也算一次完成的使用记录。刚开始打算采用监听home键点击,寻求的方法是监听系统广播。 可以实现监听home键被点击,但有一个弊端就是点击home键app切换到后台后,在使用别的app点击home键 自己的app还是会收到这个广播,因为这是

    2024年02月11日
    浏览(37)
  • 一个面向MCU的小型前后台系统

    JxOS面向MCU的小型前后台系统,提供消息、事件等服务,以及软件定时器,低功耗管理,按键,led等常用功能模块。 gitee仓库地址为(复制到浏览器打开): 在此基础上实现了基于433的简单无线网络功能。 此项目的 设计思想 是:功能模块与硬件高度解耦,提高代码模块的可

    2024年02月09日
    浏览(43)
  • 【jenkins部署】一文弄懂自动打包部署(前后台)

    软件开发中,会分多个环境,开发环境、测试环境、预发布环境、生产环境,软件部署如果是纯人工一个个通过jar的方式, 会有如下问题: 服务器过多,容易出错 修改配置,可能会存在未修改到位的情况 服务器部署权限一般只有开发服务器人才有权限,涉及到服务器的安全

    2024年02月08日
    浏览(52)
  • centos配置nginx+node前后台+mongodb

    centos 环境下安装

    2024年02月11日
    浏览(49)
  • 一套前后台全部开源的H5商城送给大家

    博主给大家推荐一套全部开源的H5电商项目 waynboot-mall 。由博主在2020年开发至今,已有三年之久。那时候网上很多的H5商城项目都是半开源版本,要么没有H5前端代码,要么需要加群咨询,属实恶心。于是博主决定自己开发一套完整的移动端H5商城,包含一个管理后台、一个前

    2024年02月02日
    浏览(81)
  • 前后台传递参数中出现+、-、=、%、&、#、空格等字符的解决思路

    一、描述问题 前后台传输数据多样化,可能会出现特殊字符的情况,比如传递的参数中含有+、空格、=、%等字符,遇到这样的情况我们该如何解决呢? 二、问题分析 前后台特殊字符对其编码,原因可能是这些特殊字符对于前后台传递参数的时候,有其特殊的用途,比如url中

    2024年01月17日
    浏览(81)
  • 【SpringMVC】统一异常处理 前后台协议联调 拦截器

    1. 问题描述 在讲解这一部分知识点之前,我们先来演示个效果,修改BookController类的 getById 方法 重新启动运行项目,使用PostMan发送请求,当传入的id为1,则会出现如下效果: 前端接收到这个信息后和之前我们约定的格式不一致,这个问题该如何解决? 在解决问题之前,我们

    2024年02月11日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包