- 介绍
- 1 HTML部分
- 1.1 实现文件的选择与上传(使用Element-ui的``标签)
- 1.2 实现文件上传的一个进度条(使用Element-ui的``标签)
- 2 JavaScript部分
- 2.1 参数部分
- 2.2 方法部分
- 2.2.1 开始上传之前的验证方法
- 2.2.2 开始上传文件
- 2.2.3 上传文件
- 2.2.4 获取文件的MD5
- 2.2.5 获取文件分片数量/文件分片List
- 2.2.6 构建请求体
- 2.2.7 获取请求的返回值
- 2.2.8 重置请求参数
- 本文全部更新已完成
实现一个大文件分片上传的功能,仅支持单个文件的分片逐一上传,对上传的文件没有大小限制,但上传速度没有过互联网的测验,本地测验,上传速度约等于本地文件拷贝速度,有很多弊端,但可满足基本的大文件分片上传功能。
本文仅为前端vue,如需后台java,请移步我的另一篇文章:大文件分片上传的后台方法(Java)
1 HTML部分没啥可说的,使用Element-ui的
标签,上代码
标签)
<template>
<div>
<el-upload
:accept="accept"
:auto-upload="auotUpload"
action=""
:multiple="multiple"
:http-request="prepareUpload"
>
el-upload>
div>
template>
1.2 实现文件上传的一个进度条(使用Element-ui的
标签)
<template>
<div>
<el-card style="margin-top:2px;margin-lift:2%;margin-right:2%;height:40px;" >
<div style="margin-top:4px;margin-lift:2%">
<el-row>
<el-col :span="8">
<div style="margin-top:8px;margin-lift:2%">{{fileInfo.fileName}}div>
el-col>
<el-col :span="8">
<div style="margin-top:8px;margin-lift:2%">
<el-progress :percentage="percentage" type="line" :text-inside="true" :stroke-width="strokeWidth" :status="status"/>
div>
el-col>
<el-col :span="3">
<div style="margin-top:8px;margin-lift:2%">{{fileInfo.fileName}}/{{fileInfo.allNum}}Mbdiv>
el-col>
<el-col :span="5">
<el-button type="text" @click="usable=1">暂停div>
<el-button type="text" @click="prepareUpload(0)">继续div>
el-col>
el-row>
div>
el-card>
div>
template>
2 JavaScript部分
2.1 参数部分
data(){
return:{
//文件参数
fileInfo:{
md:"", //文件唯一标识码,类似MD5,如果拥有MD5技术可以替换为MD5
allNum:"", //文件分片后的全部分片数量
successNum:"", //上传成功的分片文件数量
fileName:"", //文件名
detail:"", //文件描述
path:"", //文件存储路径
type:0, //文件状态,0为未上传完成,1为上传完成,用于查询时是否展示
parentId:"", //文件类型的父类,用于查询时是否展示
fileTypeId:"" //文件类型,用于查询时是否展示
},
SIZE:1024*1024, //控制单个分片文件的大小
//以下为`el-upload`标签使用的参数
accept:"", //控制默认选择的文件类型
autoUpload:true, //是否在选择文件后自动开始上传文件
uploadURL:"", //文件上传地址这里为空,因为不需要使用这个参数,但是必须有
baseURL:"", //文件上传地址的根地址,后边方法中会拼接完成完整的地址,根地址一般为:http://127.0.0.1:8080/demo/......
limit:1, //支持同时上传的文件数量
multiple:false, //是否支持文件多选
showFileList:false, //是否显示文件列表
file:"", //上传文件列表
//以下为进度条组件所用参数
strokeEidth:12, //进度条的高度
percentage:0, //进度条百分比
sataus:"exception", //进度条状态
//以下用于控制文件上传功能是否开启
usable=1
}
}
2.2 方法部分
2.2.1 开始上传之前的验证方法
prepareUpload(params){ //准备开始上传
//这里检查是否有文件正在上传,如果上传开始,则usable会被修改为0
if(this.usable===0){return this.$message({message:"当前正在执行文件上传任务,请在当前任务结束后再启动次功能",type="warning"})}
let file=params.file
//这里判断该方法是通过哪里调用的,
//如果是通过选择文件调用,则params!==0,则将文件保存到this.file
//如果是通过继续按钮调用,则params===0,则取用this.file继续执行文件上传
if(params===0){
//这里判断一下是否真的有文件在上传,如果没有,则退出任务
if(this.file===""){return this.$message({message:"当前暂无任务,无法继续",type="warning"})}
file=this.file;
this.$message({message:"文件开始继续上传",type="success"})
}else{
file=params.file;
this.file=params.file
}
//这里验证文件类型,可以根据自己需要进行验证,这里验证是否为zip或rar文件,如果不是则不允许上传
if(!(file.name.substring(file.name.length-4)===".zip"||!(file.name.substring(file.name.length-4)===".rar"){
return this.$message({message:"请选择zip或rar文件",type="warning"})
}
//这里验证文件大小,可自行修改限制
if(file.size>1024*1024*1024*3){return this.$message({message:"您无法选择一个大于3GB的文件",type="warning"})}
if(file.size<=0){return this.$message({message:"您无法选择一个大于大小为0的文件",type="warning"})}
//开始执行文件上传的前置 *** 作,将usable修改为0
this.usable=0
//修改文件名,用于文件下载时的为文件重命名后带有文件后缀,避免文件无法打开问题
if(this.fileInfo.fileName===""){
this.fileInfo.fileName=file.name
}else{
const a=this.fileInfo.fileName.substring(this.fileInfo.fileName.length-3)
if(!(this.fileInfo.fileName.substring(this.fileInfo.fileName.length-3)===".7z"||
this.fileInfo.fileName.substring(this.fileInfo.fileName.length-3)==="zip"||
this.fileInfo.fileName.substring(this.fileInfo.fileName.length-3)==="rar"||)
){
if(fileName.substring(file.name.length-3)===".7z"){
this.fileInfo.fileName+=file.name.substring(file.name.length-3)
}else{
this.fileInfo.fileName+=file.name.substring(file.name.length-4)
}
}
this.getMD5(file) //自己写的一个简易版计算文件唯一标识码,具体介绍在方法中有
this.getFileChunk(file) //获取文件分片集合,用于逐一上传
//这里写一个请求,用于判断文件是否已经上传,判断文件的状态是否已经上传完成
//这里请求方法请使用自己的,我是自己封装的请求方法
el.post("请求地址",this.fileInfo).then((result)=>result.json()).then((result)=>{
this.getResult(result) //因为很多地方需要写同样的内容,就封装了一个方法来将返回值存储起来
//判断一下文件状态,如果为1则表示服务器中存在与该文件相同的文件,则无需再次上传,执行秒传,否则继续执行
if(result.result.type===1){
//执行秒传时需要对数据库进行写入新文件名的 *** 作,所以还需要一个请求来完成
el.post("请求地址",this.fileInfo).then((result)=>result.json()).then((result)=>{
if(result.result.type===1){return this.$message({message:"秒传成功",type:"success"})
}
}else{
this.beforeUpload() //如果文件没有秒传则调用上传之前的方法
}
}
}
2.2.2 开始上传文件
开始上传之前
beforeUpload(){
//循环调用该方法,实现对fileList的逐一上传,该方法主要为避免vue的异步执行导致逻辑错误
//实现方式为通过成功上传次数与总分片数量比较判断何时return
//成功上传次数为后台返回,所以js无需做次数递增,判断依据为this.fileInfo.successNum
this.beginUpload() //调用开始文件上传方法
}
开始上传文件
beginUpload(){
//判断用户是否 *** 作了暂停按钮,如果是则终止任务
if(this.usable===1){return this.$message({message:"任务已暂停",type:"success"})}
//创建请求头
const headers=new Headers({'Csrf-Token':window,localStorage.getItem('csrfToken')})
//创建请求体
const body=this.buildBody()
//判断文件为首次上传还是继续上传,这里主要是后台对不同状态有不同 *** 作,需要分开 *** 作,不能使用同一个接口
//首次上传文件与继续上传文件两个方法的区别只有url不同,(参数也不同,但是是通过其他方法封装的,无需考虑)
if(this.fileInfo.type===0){
const url=this.baseURL+"" //这里完成完整URL的拼接
//如果文件状态为0则执行继续上传
this.uploadFile(url,headers,body)
}else if(this.fileInfo.type!==0||this.fileInfo.type!==1){
const url=this.baseURL+"" //这里完成完整URL的拼接
//如果文件状态不为0或1则执行首次上传
this.uploadFile(url,headers,body)
}
2.2.3 上传文件
uploadFile(url,headers,body){
//这里上传文件的方法我使用的fetch,如果不支持可以考虑使用ajax等,需自行修改请求方式
fetch(url,{"method":"POST","headers":headers,"body":body}).then(response=>response.json()).then(result=>{
this.getResult(result) //因为很多地方需要写同样的内容,就封装了一个方法来将返回值存储起来
//判断一下分片文件列表是否已经全部上传,如果是则提示上传成功
if((this.fileInfo.successNum===1&&this.fileInfo.allNum===1)&&this.fileInfo.type===1){
this.$message({message:"上传成功",type:"success"})
this.usable=1
this.findFileListInfo()//调用查询文件方法(该文章中没有这个文件查询方法)
return this.resetFileInfo() //调用重置数据方法并结束程序执行
}
this.beginUpload() //如果判断没有完成则再次执行文件开始上传(此处开始循环调用,直到return为止)
}
}
2.2.4 获取文件的MD5
getMD5(file){
//获取文件的最后一次修改时间并用空格拆分为数组(因为好固定内容,对这个时间做一个处理,减少MD5值的长度)
const dateList=file.lastModifiedDate.toString().split(/ /) //注意:两个/之间有空格
let date=""
for(let i=0;i<5,i++){date+=dateList[i]}
//MD是将文件名、最后一次修改时间通过一个自定义的简易HASH方法处理后用-拼接,最后在拼接-文件长度获得
this.fileInfo.md=this.HASH(file.name)+"-"+this.HASH(date)+"-"+file.size.toString()
}
HASH(info){
//定义一个编码字符集
const I64Bit_TABLE='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghizklmnopqrstuvwxyz0123456789+_`~·()()“”"\''
let hash=5381
let i=info.length-1
//判断info是String类型还是Array类型
if(typeof info=="String"){
for(li>-1;i--){
hash+=(hash<<5)+info.charCodeAt(i)
}
}else{
for(;i>-1;i--){
hash+=(hash<<5)+info[i]
}
}
let value=hsah&0x7FFFFFFF //7个F
let resultValue=""
do{resultValue+=I64Bit_TABLE[value&0x3F]}
while(value>>=6)
return resultValue
}
2.2.5 获取文件分片数量/文件分片List
getFileChunk(file){
//判断文件是否为空
if(file.size===0){return this.$message({message:"文件解析失败",type="warning"})}
//判断文件大小是否大于一个分片的大小
if(file.size>this.SIZE){
//如果文件大小大于一个分片的大小,则循环拆分文件并将每一个分片存入this.fileList
for(let i=0;i*this.SIZE<file.size;i++){
this.fileList.push(file.slice(i*this.SIZE,(i+1)*this.SIZE))
this.fileInfo.allNum=i+1
}
}else{
this.fileInfo.allNum=1
this.fileList.push(file)
}
}
2.2.6 构建请求体
buildBody(){
//创建请求体
const formData=new FormData()
//将本次请求需要用到的文件分片临时存储在const file中
const file=this.fileList[this.fileInfo.successNum]
//开始封装请求体
formData.append("md",this.fileInfo.md)
formData.append("allNum",this.fileInfo.allNum)
formData.append("successNum",this.fileInfo.successNum)
formData.append("path",this.fileInfo.path)
formData.append("type",this.fileInfo.type)
formData.append("fileName",this.fileInfo.fileName)
formData.append("detail",this.fileInfo.detail)
formData.append("size",this.fileInfo.size)
formData.append("parentId",this.fileInfo.parentId)
formData.append("fileTypeId",this.fileInfo.fileTypeId)
formData.append("file",file)
return formData
}
2.2.7 获取请求的返回值
//因为不是全部的返回值都需要用到,所以只需要获取需要的值即可,写多了还可能出现获取不到而报错,导致终止运行
getResult(result){
this.fileInfo.allNum=result.result.allNum
this.fileInfo.successNum=result.result.successNum
this.fileInfo.path=result.result.path
this.fileInfo.type=result.result.type
//下边是计算当前上传进度条的百分比,用于进度条进度显示,Math.round()实现对括号中数字的向上取整
this.percentage=Math.round(this.fileInfo.successNum/this.fileInfo.allNum*100)
}
2.2.8 重置请求参数
resultFileInfo(){
this.fileInfo.md=""
this.fileInfo.successNum=""
this.fileInfo.allNum=""
this.fileInfo.fileName=""
this.fileInfo.detail=""
this.fileInfo.path=""
this.fileInfo.type=""
this.fileInfo.parentId=""
this.fileInfo.fileTypeId=""
this.fileList=[]
this.file=""
this.percentage=0
}
本文全部更新已完成
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)