上传图片组件

简介

上传图片组件也是后台管理系统的最重要的基础组件之一,这里功能支持图片文件类型检验,图片大小检验,图片分辨率校验以及图片比列校验等功能。

主要依赖说明 (先安装,步骤略)

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
  import {
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
};