上传图片组件
简介
上传图片组件也是后台管理系统的最重要的基础组件之一,这里功能支持图片文件类型检验,图片大小检验,图片分辨率校验以及图片比列校验等功能。
主要依赖说明 (先安装,步骤略)
1 | { |
2 | "element-ui": "2.11.1", |
3 | "vue": "^2.6.10", |
4 | "vue-router": "^3.0.1" |
5 | } |
正文
1.组件
src/components/Upload.vue
1 | <template> |
2 | <div class="custom-upload"> |
3 | <input |
4 | :id="id" |
5 | type="file" |
6 | style="display: none" |
7 | name="single" |
8 | accept="image/*" |
9 | @change="onChange" |
10 | /> |
11 | <el-button |
12 | size="small" |
13 | type="primary" |
14 | :loading="loading" |
15 | @click="handleOpenFile" |
16 | > |
17 | <i class="fa fa-upload" /> |
18 | 添加图片 |
19 | </el-button> |
20 | <div v-if="tips" class="tips clear-margin-top">{{ tips }}</div> |
21 | </div> |
22 | </template> |
23 | |
24 | <script> |
25 | // 上传文件组件 |
26 | |
27 | isAppropriateResolution, |
28 | isAppRatio, |
29 | isImageFile, |
30 | isMaxFileSize |
31 | } from "@/utils/upload"; // upload.js 文件见下文 |
32 | |
33 | // 定义的接口根据自己项目更换 |
34 | import { uploadImage } from "@/api/upload"; |
35 | |
36 | import { Message } from "element-ui"; |
37 | |
38 | export default { |
39 | name: "Upload", |
40 | props: { |
41 | // 最大上传文件的大小 单位(MB) |
42 | maxFileSize: { |
43 | type: Number, |
44 | default: 2 |
45 | }, |
46 | // 提示内容 |
47 | tips: { |
48 | type: String |
49 | }, |
50 | // 图片文件分辨率的宽度 |
51 | width: { |
52 | type: Number, |
53 | width: 460 |
54 | }, |
55 | // 图片文件分辨率的高度 |
56 | height: { |
57 | type: Number, |
58 | default: 300 |
59 | }, |
60 | // 是否限制分辨率 |
61 | isResolution: { |
62 | type: Boolean, |
63 | default: false |
64 | }, |
65 | // 是否限制比列 |
66 | isRatio: { |
67 | type: Boolean, |
68 | default: false |
69 | }, |
70 | // 比列 ag: 1:1 时给 [1,1] |
71 | ratio: { |
72 | type: Array |
73 | } |
74 | }, |
75 | data() { |
76 | return { |
77 | id: "upload-input-" + +new Date(), |
78 | loading: false |
79 | }; |
80 | }, |
81 | |
82 | methods: { |
83 | // 打开文件 |
84 | handleOpenFile() { |
85 | const input = document.getElementById(this.id); |
86 | // 解决同一个文件不能监听的问题 |
87 | input.addEventListener( |
88 | "click", |
89 | function() { |
90 | this.value = ""; |
91 | }, |
92 | false |
93 | ); |
94 | // 点击input |
95 | input.click(); |
96 | }, |
97 | |
98 | // 选择好文件 |
99 | async onChange($event) { |
100 | this.loading = true; |
101 | const file = $event.target.files[0]; |
102 | if (!file) { |
103 | this.loading = false; |
104 | return Message.error("选择图片失败"); |
105 | } |
106 | |
107 | // 限制为图片文件 |
108 | if (!isImageFile(file)) { |
109 | this.loading = false; |
110 | return; |
111 | } |
112 | |
113 | // 限制文件上传大小 |
114 | if (!isMaxFileSize(file, this.maxFileSize)) { |
115 | this.loading = false; |
116 | return; |
117 | } |
118 | |
119 | try { |
120 | // 限制分辨率 |
121 | if (this.width !== 0 && this.height !== 0 && this.isResolution) { |
122 | await isAppropriateResolution(file, { |
123 | width: this.width, |
124 | height: this.height |
125 | }); |
126 | } |
127 | |
128 | // 限制比列 |
129 | if (this.isRatio && this.ratio && this.ratio.length === 2) { |
130 | await isAppRatio(file, this.ratio); |
131 | } |
132 | // 开始上传 |
133 | this.upload(file); |
134 | } catch (error) { |
135 | Message.error(error.message || "上传失败"); |
136 | console.log(error); |
137 | this.loading = false; |
138 | } |
139 | }, |
140 | |
141 | // 自定义上传 |
142 | async upload(file) { |
143 | try { |
144 | const res = await uploadImage(file); |
145 | this.$emit("subUploadSucceed", res); |
146 | Message.success("上传成功"); |
147 | this.loading = false; |
148 | } catch (error) { |
149 | this.loading = false; |
150 | console.log(error); |
151 | Message.error(error.message || "上传失败"); |
152 | } |
153 | } |
154 | } |
155 | }; |
156 | </script> |
157 | |
158 | <style lang="scss" scoped> |
159 | .custom-upload { |
160 | .tips { |
161 | margin-top: 10px; |
162 | color: red; |
163 | font-size: 12px; |
164 | } |
165 | .clear-margin-top { |
166 | margin-top: 0; |
167 | } |
168 | } |
169 | </style> |
2.使用
1 | <template> |
2 | <div> |
3 | <app-upload |
4 | tips="请上传720*294的图片" |
5 | :is-resolution="true" |
6 | :width="720" |
7 | :height="294" |
8 | @subUploadSucceed="handleUploadSucceed" |
9 | /> |
10 | <img v-if="url" :src="url" class="image-size" /> |
11 | </div> |
12 | </template> |
13 | |
14 | <script> |
15 | import AppUpload from "@/components/Upload"; |
16 | export default { |
17 | name: "Banner", |
18 | components: { |
19 | AppUpload |
20 | }, |
21 | data() { |
22 | return { |
23 | url: "" |
24 | }; |
25 | }, |
26 | methods: { |
27 | // 海报上传成功 |
28 | handleUploadSucceed(url) { |
29 | this.url = url; |
30 | } |
31 | } |
32 | }; |
33 | </script> |
34 | |
35 | <style rel="stylesheet/scss" lang="scss" scoped> |
36 | .image-size { |
37 | margin-top: 10px; |
38 | width: 150px; |
39 | height: 92px; |
40 | cursor: pointer; |
41 | } |
42 | </style> |
3.补充 src/utils/upload.js 文件
1 | import { Message } from "element-ui"; |
2 | |
3 | /** |
4 | * |
5 | * @param {file} file 源文件 |
6 | * @desc 限制为图片文件 |
7 | * @retutn 是图片文件返回true否则返回false |
8 | */ |
9 | export const isImageFile = (file, fileTypes) => { |
10 | const types = fileTypes || [ |
11 | "image/png", |
12 | "image/gif", |
13 | "image/jpeg", |
14 | "image/jpg", |
15 | "image/bmp", |
16 | "image/x-icon" |
17 | ]; |
18 | const isImage = types.includes(file.type); |
19 | if (!isImage) { |
20 | Message.error("上传文件非图片格式!"); |
21 | return false; |
22 | } |
23 | |
24 | return true; |
25 | }; |
26 | |
27 | /** |
28 | * |
29 | * @param {file} file 源文件 |
30 | * @param {number} fileMaxSize 图片限制大小单位(MB) |
31 | * @desc 限制为文件上传大小 |
32 | * @retutn 在限制内返回true否则返回false |
33 | */ |
34 | export const isMaxFileSize = (file, fileMaxSize = 2) => { |
35 | const isMaxSize = file.size / 1024 / 1024 < fileMaxSize; |
36 | if (!isMaxSize) { |
37 | Message.error("上传头像图片大小不能超过 " + fileMaxSize + "MB!"); |
38 | return false; |
39 | } |
40 | return true; |
41 | }; |
42 | |
43 | /** |
44 | * |
45 | * @param {file} file 源文件 |
46 | * @desc 读取图片文件为base64文件格式 |
47 | * @retutn 返回base64文件 |
48 | */ |
49 | export const readFile = file => { |
50 | return new Promise((resolve, reject) => { |
51 | const reader = new FileReader(); |
52 | reader.onload = e => { |
53 | const data = e.target.result; |
54 | resolve(data); |
55 | }; |
56 | reader.onerror = () => { |
57 | const err = new Error("读取图片失败"); |
58 | reject(err.message); |
59 | }; |
60 | |
61 | reader.readAsDataURL(file); |
62 | }); |
63 | }; |
64 | |
65 | /** |
66 | * |
67 | * @param {string} src 图片地址 |
68 | * @desc 加载真实图片 |
69 | * @return 读取成功返回图片真实宽高对象 ag: {width:100,height:100} |
70 | */ |
71 | export const loadImage = src => { |
72 | return new Promise((resolve, reject) => { |
73 | const image = new Image(); |
74 | image.src = src; |
75 | image.onload = () => { |
76 | const data = { |
77 | width: image.width, |
78 | height: image.height |
79 | }; |
80 | resolve(data); |
81 | }; |
82 | image.onerror = () => { |
83 | const err = new Error("加载图片失败"); |
84 | reject(err); |
85 | }; |
86 | }); |
87 | }; |
88 | |
89 | /** |
90 | * |
91 | * @param {file} file 源文件 |
92 | * @param {object} props 文件分辨率的宽和高 ag: props={width:100, height :100} |
93 | * @desc 判断图片文件的分辨率是否在限定范围之内 |
94 | * @throw 分辨率不在限定范围之内则抛出异常 |
95 | * |
96 | */ |
97 | export const isAppropriateResolution = async (file, props) => { |
98 | try { |
99 | const { width, height } = props; |
100 | const base64 = await readFile(file); |
101 | const image = await loadImage(base64); |
102 | if (image.width !== width || image.height !== height) { |
103 | throw new Error("上传图片的分辨率必须为" + width + "*" + height); |
104 | } |
105 | } catch (error) { |
106 | throw error; |
107 | } |
108 | }; |
109 | |
110 | /** |
111 | * |
112 | * @param {file} file 源文件 |
113 | * @param {array} ratio 限制的文件比例 ag: ratio= [1,1] |
114 | * @desc 判断图片文件的比列是否在限定范围 |
115 | * @throw 比例不在限定范围之内则抛出异常 |
116 | */ |
117 | export const isAppRatio = async (file, ratio) => { |
118 | try { |
119 | const [w, h] = ratio; |
120 | if (h === 0 || w === 0) { |
121 | const err = "上传图片的比例不能出现0"; |
122 | Message.error(err); |
123 | throw new Error(err); |
124 | } |
125 | const base64 = await readFile(file); |
126 | const image = await loadImage(base64); |
127 | if (image.width / image.height !== w / h) { |
128 | throw new Error("上传图片的宽高比例必须为 " + w + " : " + h); |
129 | } |
130 | } catch (error) { |
131 | throw error; |
132 | } |
133 | }; |