Vue項(xiàng)目使用Websocket大文件FileReader()切片上傳實(shí)例
大文件上傳,本地1.3G文件不到一分鐘上傳完畢
使用技術(shù)
- Vue框架
- WebSocket雙向傳輸
- FileReader讀取文件
封裝的WebSocket請(qǐng)求文件上傳方法,目前只支持單文件上傳,有研究出來多文件上傳,記得評(píng)論哦
upfile.js文件
//file.slice(起始字節(jié),終止字節(jié))與FileReader實(shí)現(xiàn)文件切片讀取
function PartFileReader(files, type, event) {
this.files = files;//inputObj.files[0]
this.type = type; //配置FileReader讀取文件的方法
this.event = event;//配置讀取文件時(shí)需要觸發(fā)的事件
this.total = files.size;//獲取文件大小
this.step = 1024 * 1024 * 10;//1MB(單片大小/一兆)
this.loaded = 0; //文件當(dāng)前讀取進(jìn)度
this.reader = new FileReader(); //實(shí)際讀取文件的FileReader對(duì)象實(shí)例
this.abort = this.reader.abort; //中斷文件讀?。梢酝ㄟ^中斷文件讀取事件保留切片數(shù)據(jù),實(shí)現(xiàn)下一次可以在原讀取位置繼續(xù)開始讀?。?
this.readPartFile(this.loaded); //開啟讀取文件
this.bindEvent();//綁定FileReader文件讀取
}
//給切片讀取對(duì)象原型上添加FileReader獲取讀取類型,開啟讀取文件
PartFileReader.prototype.readPartFile = function (start) {
if (this.files.slice) {
var file = this.files.slice(start, this.loaded + this.step);
switch (this.type) {
// 開始讀取指定的 Blob中的內(nèi)容,根據(jù)讀取方式不同,result最后返回的數(shù)據(jù)也不同
case 'readAsBinaryString' : // result屬性中將包含所讀取文件的原始二進(jìn)制數(shù)據(jù)。
this.reader.readAsBinaryString(file);
break;
case 'readAsDataURL' : // result屬性中將包含一個(gè)data: URL 格式的 Base64 字符串以表示所讀取文件的內(nèi)容。
this.reader.readAsDataURL(file);
break;
case 'readAsArrayBuffer' : // result 屬性中保存的將是被讀取文件的 ArrayBuffer 數(shù)據(jù)對(duì)象。
this.reader.readAsArrayBuffer(file);
break;
case 'readAsText' : // 一旦完成,result屬性中將包含一個(gè)字符串以表示所讀取的文件內(nèi)容。
this.readAsText(file);
break;
}
}
}
//給切片讀取對(duì)象原型上綁定FileReader對(duì)象事件
PartFileReader.prototype.bindEvent = function () {
var self = this;
this.reader.onloadstart = function (e) { // FileReader.onloadstart 該事件在讀取操作開始時(shí)觸發(fā)。
self.event.loadStart && self.event.loadStart.call(this, e);
}
this.reader.onprogress = function (e) { // FileReader.onprogress 該事件在讀取Blob時(shí)觸發(fā)。
self.event.progress && self.event.progress.call(this, e);
}
this.reader.onload = function (e) { // FileReader.onload 該事件在讀取操作完成時(shí)觸發(fā)。
// 切片讀取文件有別于非切片讀取,切片讀取的文件讀取狀態(tài)需要在每個(gè)切片讀取成功后再刷新讀取進(jìn)度
self.loaded += e.loaded;
self.event.load && self.event.load.call(this, e, self.loaded, self.total);
if (self.loaded < self.total) {
self.readPartFile(self.loaded);
}
}
this.reader.onloadend = function (e) { // FileReader.onloadend 該事件在讀取操作結(jié)束時(shí)(要么成功,要么失?。┯|發(fā)。
self.event.loadend && self.event.loadend.call(this, e);
}
this.reader.onabort = function (e) { // FileReader.onabort 該事件在讀取操作被中斷時(shí)觸發(fā)。
self.event.abort && self.event.abort.call(this, e);
}
}
function readFile(socket,file,obj) {
console.log("文件",file);
var reader = new PartFileReader(file, 'readAsArrayBuffer', {
loadStart: function (e) {
// progressTextObj.innerText = "正在讀取文件(0%)...";
},
progress: function (e) {
},
load: function (e, loaded, total) {
// 如果在讀取的基礎(chǔ)上寫上傳的話在這里獲取讀取成功的文件切片
// e.target.result //當(dāng)前文件切片的數(shù)據(jù)
socket.send(e.target.result);
// 拿到進(jìn)度,做進(jìn)度條
obj.uploadSchedule = Math.round(loaded / total * 100);
// if (obj.uploadSchedule < 100) {
// // progressTextObj.innerText = '正在讀取文件(' + precent + '%)...';
// } else
if (loaded >= total) {
// progressTextObj.innerText = '文件讀取完成(100%)';
socket.send("end");
}
},
loadend: function (e) {
},
abort: function (e) {
},
error: function (e) {
// progressTextObj.innerText = "文件讀取出錯(cuò)誤(~0v0~)";
}
})
}
// function sleep(time) {
// return new Promise(function (resolve) {
// setTimeout(resolve, time);
// });
// }
function openSocket(file,obj) {
var socket
if (typeof (WebSocket) == "undefined") {
console.log("您的瀏覽器不支持WebSocket");
} else {
console.log("您的瀏覽器支持WebSocket");
//實(shí)現(xiàn)化WebSocket對(duì)象,指定要連接的服務(wù)器地址與端口 建立連接
// var userId = document.getElementById('userId').value;
var socketUrl = "ws://192.168.1.24:8081/webSocket/" + obj.userName;
// var socketUrl = "ws://localhost:8081/webSocket/" + name;
console.log(socketUrl);
if (socket != null) {
socket.close();
socket = null;
}
socket = new WebSocket(socketUrl);
//打開事件
socket.onopen = function () {
console.log("websocket已打開",obj);
readFile(socket,file,obj)
// WebSocket鏈接成功建立,可以在這里進(jìn)行后端交互
//socket.send("這是來自客戶端的消息" + location.href + new Date());
};
//獲得消息事件
socket.onmessage = function (msg) {
var serverMsg = "收到服務(wù)端信息:" + msg.data;
console.log(serverMsg);
//發(fā)現(xiàn)消息進(jìn)入 開始處理前端觸發(fā)邏輯
};
//關(guān)閉事件
socket.onclose = function () {
console.log("websocket已關(guān)閉");
};
//發(fā)生了錯(cuò)誤事件
socket.onerror = function () {
console.log("websocket發(fā)生了錯(cuò)誤");
}
}
}
function closeSocket(socket) {if (socket != null) {
socket.close();
socket = null;
}
}
export function uploadFilesW(file,obj){
openSocket(file,obj)
}
頁面中使用
<template>
<div>
<input type="file" @change="fileChangeF($event,fileId)" />
</div>
</template>
<script>
import { uploadFilesW } from "../../util/upfile"
export default {
data(){
return {
expDialog:{ // 上傳文件彈窗
dialogVisible:false,
cloudFile:null, // 云文件
trajectoryFile:null, // 云文件
explainFile:null, // 說明文件
uploadSchedule:0, // 上傳進(jìn)度
precentState:'', // 文件上傳狀態(tài) 成功/失敗
showProgressBar:false, // 進(jìn)度條展示
uploadBtncontent:1,
isUpDisable: false, // 一個(gè)上傳完才可以上傳下一個(gè)
},
}
},
methods:{
fileChangeF(eve,type){ // 上傳文件選擇文件
console.log(eve.target.files,type);
let fi = eve.target.files
let self = this
let exp = self.expDialog
uploadFilesW(fi[0],this.expDialog)
console.log(exp);
},
}
}
</script>
新增需求:對(duì)上傳文件流進(jìn)行加密,并傳給后端做驗(yàn)證
原文連接:使用React框架 http://www.dhdzp.com/article/264653.htm
實(shí)現(xiàn)方式: fileReader讀取文件并 使用md5加密文件
還是在同一個(gè)文件中,封裝了方法進(jìn)行調(diào)用
安裝并引用spark-md5
需要先npm下載 spark-md5
npm i spark-md5 // 安裝spark-md5
還是在upfile.js文件(也可以單獨(dú)放一個(gè)文件)
import SparkMD5 from 'spark-md5';
function neverPromise(file) {
return new Promise(resolve => { // 因?yàn)槭褂胒ileReader讀取文件是異步的,所以使用promise將加密之后的值返回方便傳給后端
let fileReader = new FileReader(),
blobSlice =
File.prototype.mozSlice ||
File.prototype.webkitSlice ||
File.prototype.slice,
// chunkSize = 2097152,
// read in chunks of 2MB
chunkSize= 1024 * 1024 * 10,//1MB(單片大小/一兆)
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5(),
that = this,
md5Str ;
// console.log(Math.ceil(file.size / chunkSize),'file.size / chunkSize');
// console.log(chunks,'chunks');
fileReader.onload = function (e) {
spark.appendBinary(e.target.result);
// append binary string
currentChunk++;
if (currentChunk < chunks) { // 切片讀取完執(zhí)行
// 執(zhí)行這個(gè)方法才能短時(shí)間內(nèi)獲取到加密后的md5文件?
loadNext();
} else {
let md = spark.end();
// console.log(file,'file');
resolve(md.toUpperCase()) // 將數(shù)據(jù)返回
}
};
function loadNext () {
let start = currentChunk * chunkSize,
end = start + chunkSize >= file.size ? file.size : start + chunkSize;
console.log(blobSlice.call(file, start, end),'blobSlice.call(file, start, end)');
// readAsBinaryString這個(gè)函數(shù)看MDN上說是已經(jīng)棄用,readAsArrayBuffer來代替
// 但是后來發(fā)現(xiàn)這兩個(gè)函數(shù)讀取完文件加密后的值不同,為了跟后端同步使用了readAsBinaryString
//
fileReader.readAsBinaryString(blobSlice.call(file, start, end));
// fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
})
}
// 在使用的地方調(diào)用即可
let md5Send = await neverPromise(file)// 方法記得加async
console.log(md5Send) // 就可以拿到讀取完并加密后的字符串
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue的子組件props如何設(shè)置多個(gè)校驗(yàn)類型
這篇文章主要介紹了Vue的子組件props如何設(shè)置多個(gè)校驗(yàn)類型問題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
vue3+vite+vant項(xiàng)目下按需引入vant報(bào)錯(cuò)Failed?to?resolve?import的原因及解決
這篇文章主要給大家介紹了關(guān)于vue3+vite+vant項(xiàng)目下按需引入vant報(bào)錯(cuò)Failed?to?resolve?import的原因及解決方案,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
vue中對(duì)監(jiān)聽esc事件和退出全屏問題的解決方案
這篇文章主要介紹了vue中對(duì)監(jiān)聽esc事件和退出全屏問題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
vue-router兩種模式區(qū)別及使用注意事項(xiàng)詳解
這篇文章主要介紹了vue-router兩種模式區(qū)別及使用注意事項(xiàng),結(jié)合實(shí)例形式詳細(xì)分析了vue-router兩種模式hash模式與history模式的區(qū)別、用法與操作注意事項(xiàng),需要的朋友可以參考下2019-08-08

