宅男在线永久免费观看网直播,亚洲欧洲日产国码无码久久99,野花社区在线观看视频,亚洲人交乣女bbw,一本一本久久a久久精品综合不卡

全部
常見問題
產(chǎn)品動(dòng)態(tài)
精選推薦

大文件分片上傳的實(shí)現(xiàn)【前后臺(tái)完整版】

管理 管理 編輯 刪除


在一般的產(chǎn)品開發(fā)過程中,大家多少會(huì)遇到上傳視頻功能的需求,往往我們采用的都是對視頻大小進(jìn)行限制等方法,來防止上傳請求超時(shí),導(dǎo)致上傳失敗。這時(shí)候可能將視頻分片上傳可以對你的項(xiàng)目有一個(gè)小小的體驗(yàn)優(yōu)化。

本片文章前端是vue,后臺(tái)基于PHP進(jìn)行的分片上傳,需要的小伙伴可以借鑒。

分片上傳

1、什么是分片上傳

分片上傳,就是將所要上傳的文件,按照一定的大小,將整個(gè)文件分隔成多個(gè)數(shù)據(jù)塊(我們稱之為Part)來進(jìn)行分別上傳,上傳完之后再由服務(wù)端對所有上傳的文件進(jìn)行匯總整合成原始的文件。

2、分片上傳的場景

(1)大文件上傳

(2)網(wǎng)絡(luò)環(huán)境環(huán)境不好,存在需要重傳風(fēng)險(xiǎn)的場景

3、實(shí)現(xiàn)流程步驟

a、方案一,常規(guī)步驟、本文實(shí)現(xiàn)的步驟

將需要上傳的文件按照一定的分割規(guī)則,分割成相同大小的數(shù)據(jù)塊;

初始化一個(gè)分片上傳任務(wù),返回本次分片上傳唯一標(biāo)識;

按照一定的策略(串行或并行)發(fā)送各個(gè)分片數(shù)據(jù)塊;

發(fā)送完成后,服務(wù)端根據(jù)判斷數(shù)據(jù)上傳是否完整,如果完整,則進(jìn)行數(shù)據(jù)塊合成得到原始文件。

b、方案二

前端(客戶端)需要根據(jù)固定大小對文件進(jìn)行分片,請求后端(服務(wù)端)時(shí)要帶上分片序號和大小

服務(wù)端創(chuàng)建conf文件用來記錄分塊位置,conf文件長度為總分片數(shù),每上傳一個(gè)分塊即向conf文件中寫入一個(gè)127,那么沒上傳的位置就是默認(rèn)的0,已上傳的就是Byte.MAX_VALUE 127(這步是實(shí)現(xiàn)斷點(diǎn)續(xù)傳和秒傳的核心步驟)

服務(wù)器按照請求數(shù)據(jù)中給的分片序號和每片分塊大?。ǚ制笮∈枪潭ㄇ乙粯拥模┧愠鲩_始位置,與讀取到的文件片段數(shù)據(jù),寫入文件。

前端代碼

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;    // 上傳成功 進(jìn)度條為100%
    },
    error: (e) => {
      this.$Message.error(e.msg);  //報(bào)錯(cuò)信息
    },
    uploading: (chunk, allChunk) => {
      this.videoIng = true;   // 上傳時(shí)進(jìn)度條展示 根據(jù)需要添加
      let st = Math.floor((chunk / allChunk) * 100);  這里是用上傳的第幾片除以總片數(shù)進(jìn)行百分比計(jì)算
      this.progress = st;
    },
  });
  return false;
},
utils/upload

utils/upload

import md5 from 'js-md5' //引入MD5加密
import { upload } from '@/api/upload.js'  // 這里指前端調(diào)用接口的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) // 總片數(shù)
    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 }
    }
    // 針對每個(gè)文件進(jìn)行chunk處理
    const readChunkMD5 = async () => {
        // 針對單個(gè)文件進(jìn)行chunk上傳
        for (var i = 0; i < chunkCount; i++) {
            const { chunk } = getChunkInfo(file, i, chunkSize)
            console.log("總片數(shù)" + chunkCount)
            console.log("分片后的數(shù)據(jù)---測試:" + i)
            await uploadChunk({ chunk, currentChunk: i, chunkCount })
        }
    }
    const uploadChunk = (chunkInfo) => {
        // progressFun()
        return new Promise((resolver, reject) => {
            let config = {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            }
            // 創(chuàng)建formData對象,下面是結(jié)合不同項(xiàng)目給后端傳入的對象。
            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) //總片數(shù)
            fetchForm.append('md5', fileMD5)
            upload(fetchForm, config).then(res => {
                console.log("分片上傳返回信息:", res)
                if (res.data.code == 1) {
                    // // 結(jié)合不同項(xiàng)目 將成功的信息返回出去
                    // 下面如果在項(xiàng)目中沒有用到可以不用打開注釋
                    uploading(chunkInfo.currentChunk + 1, chunkInfo.chunkCount)
                    resolver(true)
                } else if (res.data.code == 2) {
                    if (chunkInfo.currentChunk < chunkInfo.chunkCount - 1) {
                        console.log("分片上傳成功")
                    } else {
                        // 當(dāng)總數(shù)大于等于分片個(gè)數(shù)的時(shí)候
                        if ((chunkInfo.currentChunk + 1) == chunkInfo.chunkCount) {
                            console.log("文件開始------合并成功")
                            success(res.data)
                        }
                    }
                }
            }).catch((e) => {
                error && error(e)
            })
        })
    }
    readFileMD5() // 開始執(zhí)行代碼
}

后端代碼

控制器

/**
     * 視頻分片上傳
     * @return mixed
     */
    public function videoUpload()
    {
        $data = $this->request->postMore([
            ['chunkNumber', 0],//第幾分片
            ['currentChunkSize', 0],//分片大小
            ['chunkSize', 0],//總大小
            ['totalChunks', 0],//分片總數(shù)
            ['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;
    }

在實(shí)現(xiàn)分片上傳的過程,需要前端和后端配合,比如前后端的上傳塊號的文件大小,前后端必須得要一致,否則上傳就會(huì)有問題。其次文件相關(guān)操作正常都是要搭建一個(gè)文件服務(wù)器的,比如使用fastdfs、hdfs等。

本示例代碼在電腦配置為4核內(nèi)存8G情況下,上傳24G大小的文件,上傳時(shí)間需要30多分鐘,主要時(shí)間耗費(fèi)在前端的md5值計(jì)算,后端寫入的速度還是比較快。

如果項(xiàng)目組覺得自建文件服務(wù)器太花費(fèi)時(shí)間,且項(xiàng)目的需求僅僅只是上傳下載,那么推薦使用阿里的oss服務(wù)器,其介紹可以查看官網(wǎng):

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

阿里的oss它本質(zhì)是一個(gè)對象存儲(chǔ)服務(wù)器,而非文件服務(wù)器,因此如果有涉及到大量刪除或者修改文件的需求,oss可能就不是一個(gè)好的選擇。

以上就是視頻分片上傳的前后臺(tái)的所有代碼,其中有需求小伙伴可以自行加入視頻上傳驗(yàn)證,斷點(diǎn)續(xù)傳等操作。


請登錄后查看

徐斗明 最后編輯于2023-09-13 14:16:11

快捷回復(fù)
回復(fù)
回復(fù)
回復(fù)({{post_count}}) {{!is_user ? '我的回復(fù)' :'全部回復(fù)'}}
排序 默認(rèn)正序 回復(fù)倒序 點(diǎn)贊倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理員 企業(yè)

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
{{item.is_suggest == 1? '取消推薦': '推薦'}}
沙發(fā) 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暫無簡介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打賞
已打賞¥{{item.reward_price}}
{{item.like_count}}
{{item.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回復(fù) {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打賞
已打賞¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)
查看更多
打賞
已打賞¥{{reward_price}}
3989
{{like_count}}
{{collect_count}}
添加回復(fù) ({{post_count}})

相關(guān)推薦

快速安全登錄

使用微信掃碼登錄
{{item.label}} 加精
{{item.label}} {{item.label}} 板塊推薦 常見問題 產(chǎn)品動(dòng)態(tài) 精選推薦 首頁頭條 首頁動(dòng)態(tài) 首頁推薦
取 消 確 定
回復(fù)
回復(fù)
問題:
問題自動(dòng)獲取的帖子內(nèi)容,不準(zhǔn)確時(shí)需要手動(dòng)修改. [獲取答案]
答案:
提交
bug 需求 取 消 確 定
打賞金額
當(dāng)前余額:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
請輸入 0.1-{{reward_max_price}} 范圍內(nèi)的數(shù)值
打賞成功
¥{{price}}
完成 確認(rèn)打賞

微信登錄/注冊

切換手機(jī)號登錄

{{ bind_phone ? '綁定手機(jī)' : '手機(jī)登錄'}}

{{codeText}}
切換微信登錄/注冊
暫不綁定
CRMEB客服

CRMEB咨詢熱線 咨詢熱線

400-8888-794

微信掃碼咨詢

CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
返回頂部 返回頂部
CRMEB客服