Vue实现文件上传和oss上传(带进度条和取消上传功能)和拖拽后点击按钮上传(加loading效果)
Vue实现文件上传(带进度条和取消上传功能)代码实现这里我只写了单一文件上传的demo,并且只限制了文件大小不能超过5M。如果想限制上传的文件类型,可自行在input标签中进行设置,如果想要实现多个文件一起上传,先要设置input标签属性multiple=“multiple”,然后利用循环将每一个文件数据存入到formData中,最后作为参数传给服务端进行处理。因为个人习惯原因,我先对axios进
·
Vue实现文件上传和oss上传(带进度条和取消上传功能)
Vue实现文件上传和oss上传
调后端接口的方式(带进度条和取消上传功能)
代码实现
这里我只写了单一文件上传的demo,并且只限制了文件大小不能超过5M。如果想限制上传的文件类型,可自行在input标签中进行设置,如果想要实现多个文件一起上传,先要设置input标签属性multiple=“multiple”,然后利用循环将每一个文件数据存入到formData中,最后作为参数传给服务端进行处理。
因为个人习惯原因,我先对axios进行了封装,创建service.js文件,代码如下:
import axios from "axios"; //引入axios
const instance = axios.create({
timeout: 300000,
});
// let baseURL = process.env.BASE_URL.toString();
//这里根据自己项目接口的位置自行设定
let baseURL = "http://1xx.1xx.1xx.1xx/hyr";
if (process.env.NODE_ENV == "production") {
baseURL = "http://1xx.1xx.1xx.1xx/hyr";
}
instance.defaults.baseURL = baseURL; //baseURL用于自动切换本地环境接口和生产环境接口
instance.defaults.headers.post["Content-Type"] ="application/x-www-form-urlencoded;charset=UTF-8";
instance.defaults.headers.post["Access-Control-Allow-Origin"] = "*";
instance.defaults.withCredentials = false; // 携带cookie
//上传附件axios接口封装
const upload = {
uploadFile(url, payload, cancelToken, cd) {
return instance({
url: url,
method: "post",
data: payload,
onUploadProgress: function(progressEvent) {
if (progressEvent.lengthComputable) {
cd(progressEvent);
}
},
cancelToken: cancelToken,
});
},
}
export {
upload,
axios,
}
进度条的实现: 主要依靠axios中提供的onUploadProgress函数,该函数提供了文件已上传部分的大小progressEvent.loaded和文件总大小progressEvent.total,利用这两个数据我们就可以计算出已经上传文件的进度。
在创建一个vue文件,将上述封装方法引入,代码如下:
<template>
<div class="upload-file-box">
<div class="input-line">
<div class="input-title">
<span class="name">附件上传:</span>
</div>
<label v-if="loading">
<div class="ban-button icon-btn">
<span>附件上传</span>
</div>
</label>
<label for="files" v-else>
<div class="upload-btn icon-btn">
<span>附件上传</span>
</div>
</label>
<input type="file" @change="changeFiles" class="files" id="files" />
</div>
<div class="limit-info">
支持文件格式:常用文件格式,单个文件不能超过5Mb
</div>
<div class="input-line" v-show="loading">
<div class="input-title">
<span class="name">上传状态:</span>
</div>
<div class="loading-box">
<div class="current-loading" :style="{ width: percent + '%' }"></div>
</div>
<div class="percent-val">{{ Math.round(percent) }}%</div>
<div class="cancel-btn" @click="cancelUpload">取消</div>
</div>
<div class="file-state" v-show="loading">
<div class="file-name">
<span>{{ originalName }}</span>
</div>
<div class="state">
<span>正在上传</span>
</div>
</div>
<div class="file-state" v-show="complete">
<div class="file-name">
<a :href="filePath" target="_blank">{{ originalName }}</a>
</div>
<div class="state" v-show="!!filePath">
<span>已完成</span>
</div>
</div>
</div>
</template>
<script>
import { upload } from "./service.js";
import axios from 'axios'
export default {
data() {
return {
source: null,
fileName: "",
percent: 0,
originalName: "",
uploadState: false,
filePath: "",
loading: false,
complete: false,
}
},
methods: {
changeFiles(e) {
let files = e.srcElement.files;
if (files && files.length) {
if (files[0].size / 1024 / 1024 > 5) {
alert("单个文件不能超过5MB");
document.getElementById("files").value = null;
return;
}
this.originalName = files[0].name;
this.source = axios.CancelToken.source();
let formData = new FormData();
formData.append("file", files[0]);
this.loading = true;
this.complete = false;
upload
.uploadFile(
"api/common/file/upload",
formData,
this.source.token,
(progressEvent) => {
let completeVal =
(progressEvent.loaded / progressEvent.total) * 100 || 0;
this.percent = completeVal;
this.uploadState = false;
}
)
.then((res) => {
if (res.data.status) {
this.percent = 100;
this.uploadState = true;
this.filePath = res.data.resultBody.filePath;
this.loading = false;
this.complete = true;
}else{
alert("上传失败");
this.loading = false;
this.complete = false;
}
}).catch((thrown)=>{
if(axios.isCancel(thrown)){
alert("用户取消上传");
this.loading = false;
this.complete = false;
}else{
alert("其它错误异常");
}
});
}
document.getElementById("files").value = null;
},
cancelUpload() {
//取消上传
this.source.cancel("Operation canceled by the user.");
},
},
}
</script>
<style lang="scss" scoped>
.upload-file-box {
padding-top: 30px;
padding-left: 20px;
.input-line {
height: 40px;
.input-title {
float: left;
line-height: 40px;
font-size: 14px;
text-align: right;
width: 80px;
.text {
color: #666;
}
}
.files {
display: none;
}
.input {
float: left;
width: 400px;
margin-left: 16px;
}
.icon-btn {
margin-top: 5px;
margin-left: 16px;
float: left;
height: 30px;
line-height: 30px;
padding-right: 10px;
padding-left: 10px;
background-color: #0099ff;
border-radius: 5px;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: all 0.35s;
}
.upload-btn {
&:hover {
opacity: 0.8;
}
}
.ban-button {
cursor: not-allowed;
background-color: #eeeff3;
}
.loading-box {
margin-top: 15px;
float: left;
width: 270px;
height: 10px;
background-color: #f6f6f6;
border-radius: 5px;
margin-left: 15px;
.current-loading {
height: 10px;
border-radius: 5px;
background-color: #3399cc;
/*width: 50%;*/
}
}
.percent-val {
line-height: 40px;
width: 60px;
text-align: center;
font-size: 14px;
color: #666;
float: left;
}
.cancel-btn {
line-height: 40px;
font-size: 14px;
color: #999;
text-decoration: underline;
cursor: pointer;
user-select: none;
&:hover {
color: #3399CC;
}
}
.del-btn {
line-height: 40px;
font-size: 14px;
color: #999;
text-decoration: underline;
cursor: pointer;
user-select: none;
&:hover {
color: #3399CC;
}
}
}
.limit-info {
color: #999;
font-size: 14px;
padding-left: 96px;
line-height: 34px;
height: 34px;
}
.file-state {
width: 270px;
margin-left: 95px;
overflow: hidden;
.file-name {
float: left;
width: 210px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
color: #999;
a {
color: #999;
font-size: 14px;
text-decoration: none;
&:hover {
color: #3399cc;
}
}
}
.state {
float: right;
font-size: 14px;
color: #1db396;
}
}
}
</style>
结束语
以上代码复制粘贴到自己的vue项目中就能够正常运行,如有错误或需要改进的地方还请与我联系,我将及时进行改正。
转载自 简书:FTD止水
下一步考虑 vue文件上传的多文件的进度条如何去实现
调oss上传的方式(带进度条)
封装oss方法
const OSS = require('ali-oss')
const OSSConfig = {
uploadHost: 'http://xxxxxx.oss-cn-shenzhen.aliyuncs.com', //OSS上传地址
folder: process.env.VUE_APP_OSS_FOLDER, // 上传的文件夹地址 例如 test/file/xxx/、test/img/xxx/
ossParams: {
region: 'oss-cn-shenzhen',
success_action_status: '200', // 默认200
accessKeyId: 'LxxxxxxJxxxxxxaFoxxxxxxD',
accessKeySecret: 'JxxxxxxPxxxxxxhZTxxxxxxFAxxxxxxv',
bucket: 'xxxxxx',
},
}
// 随机数函数
function random_string(len) {
len = len || 32
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
var maxPos = chars.length
var pwd = ''
for (let i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos))
}
return pwd
}
// 直接调取oss函数,返回URL结果
function uploadOSS(file) {
return new Promise(async (resolve, reject) => {
const fileName = `${OSSConfig.folder}${random_string(6)}_${file.name}`
let client = new OSS({
region: OSSConfig.ossParams.region,
accessKeyId: OSSConfig.ossParams.accessKeyId,
accessKeySecret: OSSConfig.ossParams.accessKeySecret,
bucket: OSSConfig.ossParams.bucket,
secure: true
})
const res = await client.multipartUpload(fileName, file)
if (res.name) {
resolve({
fileName: file.name,
url: `${OSSConfig.uploadHost}/${fileName}`
})
} else {
reject('OSS上传失败')
}
})
}
// 用于上传文件进度条的oss函数
function client() {
//后端提供数据
return new OSS({
region: OSSConfig.ossParams.region,
accessKeyId: OSSConfig.ossParams.accessKeyId,
accessKeySecret: OSSConfig.ossParams.accessKeySecret,
bucket: OSSConfig.ossParams.bucket,
secure: true
})
}
export {
OSSConfig,
random_string,
uploadOSS,
client
}
页面中使用
<template>
<div class="content-box">
<div class="container">
<div class="title">
点击上传图像(支持image/jpg,image/jpeg,image/png,image/gif格式图片且大小不能超过10MB)
</div>
<el-upload
:http-request="Upload"
:data="dataObj"
:multiple="false"
:show-file-list="false"
class="image-uploader"
drag
action=""
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将图片拖到此处,或
<em>点击上传</em>
</div>
</el-upload>
<el-progress v-if="isImportLoading" :percentage="progress" color="#61C5C1"></el-progress>
<img :src="imgSrc" alt="" style="width:300px;height:300px" v-if="imgSrc">
</div>
</div>
</template>
<script>
import { OSSConfig, client , random_string } from '@/utils/ossFile'
export default {
props: {},
data() {
return {
// dataObj: {}, // 存签名信息
progress: 0, //进度条
isImportLoading: false,
imgSrc: ''
}
},
created() {},
methods: {
// 是为了获取Ali配置信息的,但是封装ossFile 时已写好配置,就不需要了
// beforeUpload() {
// return new Promise((resolve, reject) => {
// //从后台获取第一步所需的数据
// getAliToken()
// .then(response => {
// this.dataObj = response.data
// resolve(true)
// })
// .catch(err => {
// console.log(err)
// reject(false)
// })
// })
// },
Upload(file) {
const that = this
//判断扩展名
const lastName= file.file.name.lastIndexOf('.')
const expandedName = file.file.name.substring(lastName + 1)
const typeNames = ['jpg', 'jpeg', 'webp', 'png', 'bmp']
if (typeNames.indexOf(expandedName) < 0) {
this.$message.error('不支持的格式!')
return
}
async function multipartUpload() {
// 上传的路径地址 + 随机数 + 文件名称
const fileName = process.env.VUE_APP_OSS_FOLDER + random_string(6) + file.file.name
//定义唯一的文件名
// client 是第一步中的 client
// progress 函数就是进度条回调的函数,提供进度条数据
client().multipartUpload(fileName, file.file, {
progress: function (p) {
that.isImportLoading= true
//获取进度条的值
// console.log(p)
that.progress = parseInt(p * 100)
}
})
.then(result => {
//下面是如果对返回结果再进行处理,根据项目需要
that.isImportLoading= false
console.log(result)
// http://xxxxxx.oss-cn-shenzhen.aliyuncs.com/xxx/xxx/rjdpXG_图片测试1.jpg
that.imgSrc = OSSConfig.uploadHost + fileName
})
.catch(err => {
console.log('err:', err)
})
}
multipartUpload()
}
}
}
</script>
<style lang="scss" scoped>
</style>
elementUI的拖拽上传,点击导入再调接口上传,单文件且覆盖上次文件(并加loading效果)
样式还没有调整,姑且先看实现效果吧
主要代码我抽离处理,不过多解释
<el-upload
class="upload-demo-xls"
drag
ref="speechDemoUpload"
action=""
:file-list="fileList"
:auto-upload="false"
:show-file-list="false"
accept=".xls,.xlsx,.csv"
:on-change="handleChange"
>
<!-- <i class="el-icon-upload"></i> -->
<div class="el-upload__text" v-if="!uploadStatus">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__text" v-else>
{{ this.file && this.file.name }}
<em>点击上传</em>
</div>
</el-upload>
// 这里封装的代码和上面加载进度条的封装函数一样
import { upload, service } from '@/utils/request'
this.fileList = []
this.file = null
// 上传的文件改变时(覆盖原来的文件)
handleChange(file, fileList){
// console.log(file);
let extension = file.raw.name.substring(file.raw.name.lastIndexOf(".") + 1);
let size = file.size / 1024 / 1024;
// let size = file.size / 1024;
// console.log(extension, extension.toLowerCase() !== "xlsx");
if (!['xlsx','xls','cvs'].includes(extension.toLowerCase())) {
this.$message.warning("文件格式不正确,请上传xls / xlsx / csv格式");
return false;
}
if (size > 10) {
this.$message.warning("文件过大,请进行拆分后分多次上传");
return false
}
// console.log(file.raw, fileList);
if (fileList.length > 0) {
this.fileList = [fileList[fileList.length - 1]] // 这一步,是展示最后一次选择的csv文件
this.file = this.fileList[0].raw
}
// console.log(this.file);
this.uploadStatus = true
},
async xlsDemoExport(){
if (!this.file) {
return this.$message.error('请上传文件')
}
// await this.$refs.speechDemoUpload.submit()
const formData = new FormData()
formData.append('file', this.file)
// 调用上传接口...
this.$loading.show()
// `${process.env.VUE_APP_BASE_BRAND_API}/v1/inspectionSpeechArt/importExcel
upload.uploadFile('http://10.44.62.108:31009/v1/inspectionSpeechArt/importExcel', formData).then(res => {
console.log(res);
this.exportStatus = true
this.tableData = res.item.failList.map(item => {
let obj = {}
obj.lineNum = item.lineNum
obj.failReason = item .failReason
obj.projectCode = item. projectCode
return obj
})
this.failNum = res.item.failNum
this.successNum = res.item.successNum
this.$loading.hide()
}).catch((error) => {
console.log(error);
this.$message.error('上传失败,请稍后再试或联系IT解决')
this.$loading.hide()
})
},
// 样式调整
<style lang="scss" scoped>
::v-deep .el-list-enter-active,
::v-deep .el-list-leave-active {
transition: none;
}
::v-deep .el-list-enter,
::v-deep .el-list-leave-active {
opacity: 0;
}
::v-deep .el-upload-list {
height: 40px;
}
</style>
更多推荐
已为社区贡献21条内容
所有评论(0)