一.文件上传
文件上传是在前端开发过程中很常见的功能,这里介绍的是vue2中常使用的文件上传方式,适用于引入element-ui组件的情况下,使用的是element中的el-upload组件。
1.文件上传组件
把文件上传的方法封装成组件,这样后面使用的时候仅需要两三行代码即可,这里文件类型、大小、图片数量等的参数后续可以自己定义,这些参数放入props中。
<template>
<div class="component-upload-image">
<el-upload
:action="uploadImgUrl"
list-type="picture-card"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
name="file"
:on-remove="handleRemove"
:show-file-list="true"
:headers="headers"
:file-list="fileList"
:on-preview="handlePictureCardPreview"
:class="{hide: this.fileList.length >= this.limit}"
>
<i class="el-icon-plus"></i>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
<el-dialog
:visible.sync="dialogVisible"
title="预览"
width="800"
append-to-body
>
<img
:src="dialogImageUrl"
style="display: block; max-width: 100%; margin: 0 auto"
/>
</el-dialog>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
export default {
props: {
value: [String, Object, Array],
// 图片数量限制
limit: {
type: Number,
default: 5,
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["png", "jpg", "jpeg"],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
}
},
data() {
return {
dialogImageUrl: "",
dialogVisible: false,
hideUpload: false,
// 本地
// baseUrl: 'http://192.168.0.194:9000',
// 线上
baseUrl: process.env.VUE_APP_BASE_API,
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: []
};
},
watch: {
value: {
handler(val) {
if (val) {
// 首先将值转为数组
const list = Array.isArray(val) ? val : this.value.split(',');
// 然后将数组转为对象数组
this.fileList = list.map(item => {
if (typeof item === "string") {
if (item.indexOf(this.baseUrl) === -1) {
item = { name: this.baseUrl + item, url: this.baseUrl + item };
} else {
item = { name: item, url: item };
}
}
return item;
});
} else {
this.fileList = [];
return [];
}
},
deep: true,
immediate: true
}
},
computed: {
// 是否显示提示
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
},
methods: {
// 删除图片
handleRemove(file, fileList) {
const findex = this.fileList.map(f => f.name).indexOf(file.name);
if(findex > -1) {
this.fileList.splice(findex, 1);
this.$emit("input", this.listToString(this.fileList));
}
},
// 上传成功回调
handleUploadSuccess(res) {
this.fileList.push({ name: res.fileName, url: res.fileName });
this.$emit("input", this.listToString(this.fileList));
this.loading.close();
},
// 上传前loading加载
handleBeforeUpload(file) {
let isImg = false;
if (this.fileType.length) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
isImg = this.fileType.some(type => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
} else {
isImg = file.type.indexOf("image") > -1;
}
if (!isImg) {
this.$message.error(
`文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
);
return false;
}
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
this.loading = this.$loading({
lock: true,
text: "上传中",
background: "rgba(0, 0, 0, 0.7)",
});
},
// 文件个数超出
handleExceed() {
this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
},
// 上传失败
handleUploadError() {
this.$message({
type: "error",
message: "上传失败",
});
this.loading.close();
},
// 预览
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
// 对象转成指定字符串分隔
listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
strs += list[i].url.replace(this.baseUrl, "") + separator;
}
return strs != '' ? strs.substr(0, strs.length - 1) : '';
}
}
};
</script>
<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
::v-deep.hide .el-upload--picture-card {
display: none;
}
// 去掉动画效果
::v-deep .el-list-enter-active,
::v-deep .el-list-leave-active {
transition: all 0s;
}
::v-deep .el-list-enter, .el-list-leave-active {
opacity: 0;
transform: translateY(0);
}
</style>
可以在main.js中全局引入,直接在页面中使用即可,或使用时单独引入。
// 全局引入图片上传组件
import ImageUpload from "@/components/ImageUpload/index"
// 全局组件挂载
Vue.component('ImageUpload', ImageUpload)
页面中使用
<el-form-item label="卡展示图" prop="cardImg">
<ImageUpload v-model="form.cardImg" :limit="1"/>
</el-form-item>
2.实现手动上传
当上传文件需要与表单数据一起传送到服务器时,可设置·:auto-upload="false"进行手动上传,只有当点击确定时调用submit方法文件才会被传送。
<el-dialog
title="数据批量导入"
:visible.sync="upload.open"
width="500px"
append-to-body
>
<div style="padding-left: 50px">
<el-upload
class="upload-demo"
drag
:action="upload.url"
multiple
ref="upload"
:headers="upload.headers"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
accept=".xlsx, .xls"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<!-- <div class="el-upload__text">仅支持扩展名为“.xlsx”文件上传,限2M</div> -->
<p style="font-size:15px;cursor:pointer;color:#409eff" slot="tip" @click="downTemplateBtn">
点击下载公司注册导入模板.xlsx
</p>
</el-upload>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="upload.open = false">取 消</el-button>
<el-button type="primary" @click="submitFileForm">确 定</el-button>
</div>
</el-dialog>
绑定数据
upload: {
// 是否显示弹出层(用户导入)
open: false, // 弹出层标题(用户导入)
title: "批量导入", // 是否禁用上传
isUploading: false, // 是否更新已经存在的用户数据
updateSupport: 0, // 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() }, // 上传的地址
url: process.env.VUE_APP_BASE_API + "/businessAdmin/company/collection/import",
},
上传到服务器
submitFileForm() {
this.$refs.upload.submit();
},
3.formData方式上传
form表单是比较传统的上传方法,但有一些限制,提交方式必须是post,因为get通过URL提交数据,大小一般不超过2kb,而post是没有大小限制的;enctype属性必须为"multipart/form-data"。
<form action="<%=path %>/testFormUpload" enctype="multipart/form-data" method="post" target="targetFrame">
<input type="file" name="form_file">
<button type="submit" >form上传</button>
</form>
使用ajax和formData结合的方式可实现异步上传,且更具灵活性。当提交的表单中有文件时,常采用这种方式。
创建一个FormData对象
let formData = new FormData();
将文件使用append属性追加添入
formData.append("avatarfile", data);
uploadAvatar(formData).then((response) => {
this.open = false;
this.options.img = "https://file.aqrcgj.com" + response.imgUrl;
console.log(this.options.img);
this.$modal.msgSuccess("修改成功");
this.visible = false;
this.$emit("getupdata", response.imgUrl);
});
二.文件下载
1.二进制流格式
文件下载接口使用responseType: 'arraybuffer',返回二进制流图片数据格式。
注:当请求设置responseType: 'arraybuffer'时
请求成功时,返回文件流,正常导出文件。
请求失败时,返回的500错误信息也会被转成流。
解决方案:将已转为arraybuffer类型的数据转回Json对象,然后进行判断
async downloadFile (file) {
let res = await this.$axios.post(this.API.order.tradeImpExcle, { responseType: "arraybuffer" });
if (!res) return;
try {
//如果JSON.parse(enc.decode(new Uint8Array(res.data)))不报错,说明后台返回的是json对象,则弹框提示
//如果JSON.parse(enc.decode(new Uint8Array(res.data)))报错,说明返回的是文件流,进入catch,下载文件
let enc = new TextDecoder('utf-8')
res = JSON.parse(enc.decode(new Uint8Array(res.data))) //转化成json对象
if (res.Status == "true") {
this.refresh()
this.$message.success(res.Msg)
} else {
this.$message.error(res.Result)
}
} catch (err) {
const content = res.data;
const blob = new Blob([content]);
let url = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
link.setAttribute(
"download",
res.headers["content-disposition"].split("=")[1]
);
document.body.appendChild(link);
link.click();
}
}
2.blob格式
设置请求头 responseType: ‘blob’
解决方案:将已转为blob类型的数据转回Json对象,然后进行判断
if (response.data.type==="application/json") {
const reader=new FileReader()
reader.readAsText(response.data, 'utf-8');
reader.onload=function(){
const{msg}=JSON.parse(reader.result)
this.messages=msg
_this.$message({
message: this.messages,
type: "error",
});
}
}
3.file-saver方法
安装:npm install file-saver --save
import { saveAs } from 'file-saver'
export function downloada(data, filename) {
// data文件流 filename文件名加后缀
const blob = new Blob([data])
saveAs(blob, decodeURI(filename))
}
下载时调用方法
getFileDown(row.url).then(res=>{
downloada(res,row.name)
})
4.生成 URL 下载方式(canvas )
下载echart表图片
const downloadEchart = () => {
// 获取画布图表地址信息
const img = new Image();
img.src = chart.value.getDataURL({
type: "png",
pixelRatio: 1,
backgroundColor: "#fff",
});
// 当图片加载完成后,生成 URL 并下载
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.drawImage(img, 0, 0, img.width, img.height);
const link = document.createElement("a");
link.download = `业绩柱状图.png`;
link.href = canvas.toDataURL("image/png", 0.9);
document.body.appendChild(link);
link.click();
link.remove();
}
};
};
三:文件预览
lookCloud(row){
const url = this.UTILURL.YU_URL_LO_MY + row.fileAddress;
const baseId = Base64.encode(url);
window.open(this.UTILURL.YU_URL_JIE_MY + encodeURIComponent(baseId));
},
this.UTILURL.YU_URL_LO_MY为文件地址前缀,this.UTILURL.YU_URL_JIE_MY 为文件服务器地址,以此访问图片。
注:使用window.open(URL,name,specs,replace)实现,用于打开一个新的浏览器窗口