监听进程的方法onprogress
1 )文件读取的progress事件属于FileReader对象。
2 )下载的progress事件属于XMLHttpRequest对象。
3 )上传的progress事件属于XMLHttpRequest.upload对象。
本案例主要实现了文件读取
、文件上传
进度的可视化显示
demo功能
- 可添加并上传多个文件,每添加一个文件,会进行读取与上传,上传后存储文件与文件信息缓存于
files
数组中; - 文件添加后,先显示读取进度条,读取100%后开始上传并显示上传进度条;
- 读取文件时,文件标题会显示
文件读取中...
,进度条为文件读取进度; - 上传文件时,文件标题更新为具体文件名称与已上传的实时数据量,动态更新上传量,进度条为上传进度;
- 上传完毕,自动隐藏进度条;
- 注意点1:页面上只有一个
input[type=file]
框,但为了存储多个文件的二进制数据,这里将二进制数据缓存到files
数组中,input[type=file]
框只作为实时数据的处理使用; - 注意点2:读取进度在实际应用中是可以省略的,本demo中读取后会将文件二进制数据缓存如files数组中,如果省略这步,可以不用读取文件。在这里主要用于体验
文件读取进度的显示
和缓存数据;
页面与样式
<ul>
<li v-for="(file, n) in files" v-show="file.readPercent" class="file_item">
<span class="file_name" v-if="!file.fileName">
文件读取中...
</span>
<span class="file_name" v-else>
{{ file.fileName }}({{ file.uploadSize}})
</span>
<div class="progress_bar" v-show="file.readPercent !== '100%'">
<div :class="['percent', 'bar' + n]" :style="'width:' + file.readPercent"></div>
</div>
<div class="progress_bar" v-show="file.uploadPercent !== '100%'">
<div :class="['upload', 'bar' + n]" :style="'width:' + file.uploadPercent"></div>
</div>
<button @click="del(n)" class="del iconfont icon-delete" v-show="file.uploadPercent === '100%'"></button>
</li>
<li>
<input type="file" name="file" id="file" ref="file" @change="handleFileSelect" v-show="false"/>
<label for="file" style="color: #4C84FF;font-size: 14px;line-height: 32px;cursor: pointer;">Add attachment</label>
</li>
</ul>
<style>
ul {
background-color: #f2f2f2;
border-radius: 4px;
overflow: hidden;
border: 1px solid #ddd;
padding-left: 0;
}
ul::before {
content: 'Attachment';
height: 32px;
line-height: 32px;
display: block;
padding-left: 11px;
}
ul li.file_item {
margin-bottom: 1px;
}
ul li {
list-style: none;
position: relative;
background-color: #fff;
height: 32px;
padding-left: 11px;
overflow: hidden; /* 解决margin塌陷 */
}
ul li .file_name {
font-size: 14px;
color: #111;
font-weight: bolder;
line-height: 32px;
position: absolute;
}
ul li .del {
position: absolute;
right: 8px;
top: 6px;
font-size: 19.8px;
width: 19.8px;
height: 19.8px;
cursor: pointer;
padding: 0;
border: 0;
color: #666;
}
.progress_bar {
background-color: #fff;
border-radius: 100px;
overflow: hidden;
margin: 12px 166px 12px 206px;
border: 1px solid #ddd;
line-height: 8px;
height: 8px;
/* font-size: 14px; */
clear: both;
opacity: 1;
-moz-transition: opacity 1s linear;
-o-transition: opacity 1s linear;
-webkit-transition: opacity 1s linear;
}
.progress_bar.loading {
opacity: 1.0;
}
.progress_bar .percent, .progress_bar .upload {
background-color: #aaa;
height: 100%;
width: 0;
}
</style>
方法实现
每选择一次文件,files数组中添加一项占位,视图更新显示该条数据处理情况;
handleFileSelect(e) {
// 加入this.files
this.files.push({})
this.$nextTick(tick=>{
this.getFile(e.target)
})
},
读取文件后显示文件名
getFile(target) {
if (!target.files[0]) return;
let filevalue = target.value;
let index = filevalue.lastIndexOf('.');
let file = {}
file.fileName = target.files[0].name
file.filesExtension = filevalue.substring(index)
let self = this
this.readFile(target.files[0]).then(res => {
file.fileData = res // 缓存读取的二进制数据
Object.assign(self.files[self.files.length - 1], {
fileName: file.fileName, // 文件名
filesExtension: file.filesExtension, // 扩展名
fileData: file.fileData,
readPercent: '100%'
})
self.upload(target.files[0])
self.$forceUpdate()
}).catch(e => {
console.log(e)
})
},
readFile(file) {
let self = this;
return new Promise(function(resolve, reject) {
let fileCard = self.files[self.files.length - 1];
// 在选择新文件后重置进度指示器
self.$set(fileCard, 'readPercent', '0%');
let reader = new FileReader();
reader.onprogress = self.updateProgress; // 更新进度条
reader.onerror = function (e) {
switch(e.target.error.code) {
case e.target.error.NOT_FOUND_ERR:
reject('文件没找到');
break;
case e.target.error.NOT_READABLE_ERR:
reject('文件不可读');
break;
default:
reject('读取文件时出错');
};
}
reader.onloadend = function (res) {
// 确保进度条最后显示100%
self.$set(fileCard, 'readPercent', '100%');
resolve(res.target.result)
}
// 将文件作为二进制数组读入
reader.readAsArrayBuffer(file);
})
},
监听读取文件进度条
updateProgress(e) {
// e 是一个 ProgressEvent.
if (e.lengthComputable) {
let percentLoaded = ((e.loaded / e.total) * 100).toFixed(2);
let fileCard = this.files[this.files.length - 1];
this.$set(fileCard, 'fileSize', this.renderSize(e.loaded))
// 更新进度条长度
if (percentLoaded < 100) {
this.$set(fileCard, 'readPercent', percentLoaded+ '%');
}
}
},
上传文件并监听上传进度
upload(fileObj) {
let formFile = new FormData();
formFile.append("attachment", fileObj); //加入文件对象
let fileCard = this.files[this.files.length - 1];
$.ajax({
url: window.location.origin + '/upload',
data: formFile,
type: "post",
dataType: "json",
cache: false,//上传文件无需缓存
processData: false,//用于对data参数进行序列化处理 这里必须false
contentType: false, //必须
xhr: () => {
let xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.onprogress = (e) => {
let percent=e.loaded/e.total;//文件上传百分比
if (percent <= 1) {
this.$set(fileCard, 'uploadPercent', (percent * 100).toFixed(2) + '%')
this.$set(fileCard, 'uploadSize', this.renderSize(e.loaded))
}
};
}
return xhr;
},
error: (error) => {
console.log(error)
},
success: (result) => {
this.$set(fileCard, 'uploadPercent', '100%')
this.$set(fileCard, 'filePath', result.file.filepath) // 添加接口返回的文件信息,如果没有可以忽略
},
complete: ()=> {
this.clearFile() // 上传完毕清空input[type=file]
}
})
},
清空input[type=file]
clearFile() {
this.$refs.file.value = ''
},
文件大小的“可视化”,把比特值换算成常用单位显示
renderSize(value){
if(null==value||value==''){
return "0 Bytes";
}
let unitArr = new Array("Bytes","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB");
let index=0;
let srcsize = parseFloat(value);
index=Math.floor(Math.log(srcsize)/Math.log(1024));
let size =srcsize/Math.pow(1024,index);
size=size.toFixed(2);//保留的小数位数
return size+unitArr[index];
},
删除文件
del(n) {
this.files.splice(n, 1)
},
demo下载
体验要点
由于我的demo是本地服务器,上传速度会比较快。而且真实的上传和文件读取速度可能比你想象的要快,所以小文件的进度可能会一闪而过。
【建议:可以将网络调成slot3G,这样多数文件上传进度就都可以看到了。至于文件读取进度,需要上传较大的文件才可以看到。】
另外,建议测试完毕后,清空upload文件件里上传的文件,并且清空废纸篓/回收站。避免给电脑存储增加负担。
欢迎指教
即学即分享,还请各位大佬多多指出优化与问题,我才能进步!