摘要:大文件上傳的實現(xiàn)準(zhǔn)備階段大文件上傳的過程中需要用到發(fā)布訂閱模式監(jiān)聽上傳的進度發(fā)布訂閱模式的實現(xiàn)事件棧獲取事件對應(yīng)棧的索引事件類型對應(yīng)棧的索引不存在為已有事件類型處理棧監(jiān)聽事件自定義事件類型事件處理函數(shù)已存在事件類型處理直接把相應(yīng)的處理函
大文件上傳的實現(xiàn)
準(zhǔn)備階段:
大文件上傳的過程中需要用到發(fā)布訂閱模式監(jiān)聽上傳的進度
發(fā)布訂閱模式的實現(xiàn)
export default class { // 事件棧 eventStacks = [{ eventType: "", handlers: [] }]; /** * 獲取事件對應(yīng)棧的索引 * * @param {string} eventType 事件類型 * @return {number} stackIndex 對應(yīng)棧的索引 不存在為-1 */ indexOf(eventType) { const eventStacks = this.eventStacks; // 已有事件類型處理棧 let stackIndex = -1; for (let i = 0; i < eventStacks.length; i++) { const eventStack = eventStacks[i]; if (eventStack.eventType === eventType) { stackIndex = i; break; } } return stackIndex; }; /** * 監(jiān)聽事件 * * @param {string} eventType 自定義事件類型 * @param {Function} handler 事件處理函數(shù) */ on(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0) { // 已存在事件類型處理 直接把相應(yīng)的處理函數(shù)入棧 this.eventStacks[index].handlers.push(handler); } else { // 不存在事件,把對應(yīng)的事件處理入棧 const newEventStack = { eventType, handlers: [handler] }; this.eventStacks.push(newEventStack); } }; /** * 觸發(fā)對應(yīng)的事件 * * @param {string} eventType 自定義事件類型 * @param {Object} params 參數(shù)對象 */ emit(eventType, params = {}) { this.execEvent(eventType, params); }; /** * 執(zhí)行對應(yīng)的事件 * * @param {string} eventType 自定義事件類型 * @param {Object} params 參數(shù)對象 */ execEvent(eventType, params = {}) { const index = this.indexOf(eventType); if (index < 0) { return; } const handlers = this.eventStacks[index].handlers; for (let i = 0; i < handlers.length; i++) { const currentHandler = handlers[i]; if (currentHandler && typeof currentHandler === "function") { currentHandler(params); } } }; /** * 解除對應(yīng)的事件 * * @param {string} eventType 事件類型 * @param {Function} handler 事件處理器 必須是引用傳進來 使用對象引用相等判斷 */ offHandler(eventType, handler) { const index = this.indexOf(eventType); if (index >= 0 && this.eventStacks[index].handlers.length) { // 存在,并且已經(jīng)入棧 const handlers = this.eventStacks[index].handlers; this.eventStacks[index].handlers = handlers.filter(currentHandler => { return currentHandler !== handler }); } } }
文件是基于Blob BlobMDN文檔
Blob 類型可以使用slice截取一段 基于這點 我們就可以吧大文件拆分成很多小的文件塊上傳 前端實現(xiàn)如下:
import superagent from "superagent"; import eventEmitter from "../util/eventEmitter"; // 上傳單個文件 export const uploadFile = (file, url) => { const formData = new FormData(); formData.set("image", file); console.log(file.size); return superagent.post(url) .send(formData); }; // 獲取文件分塊之后的數(shù)量 const getFragmentNum = (file, fragmentSize) => { // fragmentSize 拆分文件的大小 return Math.ceil(file.size / fragmentSize); }; // 獲取文件碎片 const getFileFragment = (file, start, end, fragmentSize) => { return file.slice(start * fragmentSize, end * fragmentSize); }; const getSlicedFiles = (file, fragmentSize) => { const slicedFiles = []; // 獲取文件分塊的數(shù)量 const fragmentNum = getFragmentNum(file, fragmentSize); // 獲取所有的分塊文件 for (let i = 0; i < fragmentNum; i++) { const currentFileFragment = getFileFragment(file, i, i + 1, fragmentSize); slicedFiles.push(currentFileFragment); } return slicedFiles; }; // 多文件上傳 export class uploadBigFile extends eventEmitter { constructor(file, url, fragmentSize) { super(); // 拆分的文件數(shù)組 const slicedFiles = getSlicedFiles(file, fragmentSize); // 正在上傳的模塊 this.currentIndex = 0; this.uploadSlicedFiles(slicedFiles, url); }; /** * 上傳拆分后的文件 * * @param {Array} files 文件數(shù)組 * @param {string} url 上傳的地址 */ uploadSlicedFiles(files, url) { uploadFile(files[this.currentIndex], url) .then(({body}) => { this.emit("process", { currentIndex: this.currentIndex, res: body, progress: (this.currentIndex + 1) / files.length }); this.currentIndex++; if (this.currentIndex < files.length) { this.uploadSlicedFiles(files, url); } }) .catch(err => { console.log(err); }); }; }
后端實現(xiàn)如下:
const express = require("express"); const router = express.Router(); const multer = require("multer"); const upload = multer(); // multer用于接受formdata router.post("/upload", upload.single("image"), (req, res) => { res.send({ code: 0, file: req.file.size }); }); module.exports = router;
效果如圖 :
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105510.html
摘要:話前上傳大文件上傳的教程網(wǎng)上很多但是大部分沒給出一個比較完整的出來這個博客給出的是前后端一套完整的解決方案其中前端沒有使用第三方上傳庫希望能幫到有同樣需求的朋友們大文件分片上傳的好處在這里就不用多說了之前不管是上傳單文件還是分片文件上傳都是 話前 上傳大文件上傳的教程網(wǎng)上很多, 但是大部分沒給出一個比較完整的出來, 這個博客給出的是前后端一套完整的解決方案, 其中前端沒有使用第三方上傳...
摘要:大文件上傳主要分為三部分,預(yù)上傳,分塊上傳,合并上傳??梢詳U展此對象來控制上傳頭部。是完成最終的大文件合并上傳。修改可以控制發(fā)送哪些攜帶數(shù)據(jù)。 由于業(yè)務(wù)需要,需要上傳大文件,已有的版本無法處理IE版本,經(jīng)過調(diào)研,百度的 webuploader 支持 IE 瀏覽器,而且支持計算MD5值,進而可以實現(xiàn)秒傳的功能。 大文件上傳主要分為三部分,預(yù)上傳,分塊上傳,合并上傳。 預(yù)上傳:計算MD5值...
摘要:分片上傳主要是前端將一個較大的文件分成等分的幾片,標(biāo)識當(dāng)前分片是第幾片和總共幾片,待所有的分片均上傳成功的時候,在后臺進行合成文件即可。 一、前言 在網(wǎng)站開發(fā)中,經(jīng)常會有上傳文件的需求,有的文件size太大直接上傳,經(jīng)常會導(dǎo)致上傳過程中耗時太久,大量占用帶寬資源,因此有了分片上傳。 分片上傳主要是前端將一個較大的文件分成等分的幾片,標(biāo)識當(dāng)前分片是第幾片和總共幾片,待所有的分片均上傳成...
showImg(https://segmentfault.com/img/bVbs1lu?w=675&h=221); 關(guān)于大文件上傳 思路 使用js讀取form表單中選擇的file,計算文件md5值,并上傳md5值到服務(wù)端,檢查文件是否已上傳過(類似秒傳功能) 若文件未上傳過,按照其大小切成1MB大小的塊,小于1MB的不用切 用ajax異步提交切好的塊上傳至服務(wù)端(一個塊一個請求,不阻塞,多線程...
showImg(https://segmentfault.com/img/bVbs1lu?w=675&h=221); 關(guān)于大文件上傳 思路 使用js讀取form表單中選擇的file,計算文件md5值,并上傳md5值到服務(wù)端,檢查文件是否已上傳過(類似秒傳功能) 若文件未上傳過,按照其大小切成1MB大小的塊,小于1MB的不用切 用ajax異步提交切好的塊上傳至服務(wù)端(一個塊一個請求,不阻塞,多線程...
閱讀 1116·2021-11-24 10:24
閱讀 2600·2021-11-22 13:54
閱讀 1009·2021-09-24 09:55
閱讀 3608·2019-08-30 15:54
閱讀 1325·2019-08-30 15:44
閱讀 1104·2019-08-30 14:23
閱讀 3210·2019-08-29 13:45
閱讀 1290·2019-08-29 11:19