前言:
最近再对公司前后端不分离的【 C#、Winform 】系统进行【 Java、Web 】重构,为了保持客户使用习惯所以要高度还原,其中有一个功能就是上传图片时,以文件夹进行上传,要读取文件夹内所有的图片!
看了挺多大神的博客,也看了【 Element-UI 】 的 el-upload 发现都没有合适的,只能利用 【input 标签的 type 属性】来实现这个需求了!!!
撸代码之前还是先了解一下 input 标签的【webkitdirectory】属性,今天也是主要依赖 这个属性来帮助我们实现需求!

A Boolean indicating whether or not to only allow the user to choose a directory (or directories, if multiple is also present)
一个布尔值,指示是否只允许用户选择一个目录(如果同时存在多个目录,则为一个或多个目录)
这是 MDN 上对 【 webkitdirectory 】属性的解释。一句话,非常通透!

这段话就不做翻译了,大概我们可以看出来,【webkitdirectory 】属性的兼容性不是特别好,所以对【ie6,ie7.....】兼容的小伙伴要慎用....
因为用的是 【Vue2.0】的技术栈,所以演示就直接用 Vue 来做演示了!

唯一的注意点就是:这里通过【v-show 隐藏 DOM 节点,然后用 ref 获得节点去调用原生 input 的方法】,然后通过 input change 是获取的 e.target 去进行解析;
- <template>
- <div class="receive-img">
- <input
- v-show="false"
- type="file"
- ref="inputFile"
- accept="image/*"
- webkitdirectory
- @change="receiveImg"
- />
- <div class="btn">
- <button @click="chooseImgList">点击选择文件夹button>
- div>
- <div class="image-list-container">
- <div v-for="item in imageList" :key="item.imgUrl">
- <div class="image-list-item">
- <el-image fit="cover" :src="item.imgUrl">el-image>
- div>
- <div class="image-list-text">
- <div>{{ item.imgName }}div>
- <div>{{ item.size + "KB" }}div>
- div>
- div>
- div>
- div>
- template>
-
- <style lang="scss">
- .receive-img {
- width: 100%;
- height: 100%;
- .btn {
- margin: 100px auto 0 auto;
- }
- .image-list-container {
- width: 100%;
- height: 250px;
- display: flex;
- align-items: flex-start;
- justify-content: flex-start;
- border: 1px solid #d2d2d2;
- padding: 0 10px;
- .image-list-item {
- width: 210px;
- height: 210px;
- margin-left: 15px;
- text-align: center;
- }
- .image-list-text {
- width: 210px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- text-align: center;
- }
- .image-list-item:hover {
- cursor: pointer;
- }
- }
- }
- style>
通过 【 FileReader 】去读取文件的 进而读取出图片的name/path/size ;不过需要注意这里需要手动调用 【API】上传图片,演示的图片是 base64
- import uploadFormData from "@/utils/uploadFormData.js"
- export default {
- name: "ReceiveImage",
- data() {
- return {
- imageList: [],
- };
- },
- methods: {
- chooseImgList() {
- if (this.imageList.length == 0) {
- this.$refs["inputFile"].click();
- }
- },
- receiveImage (e) {
- var fmDataList = [];
- var count = e.target.files.length;
- for (let item in e.target.files) {
- let reader = new FileReader();
- const file = e.target.files[item];
- if (typeof file == 'object' && (file.name.indexOf('jpg') > -1 || file.name.indexOf('png') || file.name.indexOf('jpeg'))) {
- reader.readAsDataURL(e.target.files[item]);
- reader.onloadend = (e) => {
- // 这里的e.target就是reader
- let base64 = e.target.result;
- fmDataList.push({ file: base64, fileName: file.name })
- this.imageList.push({ imgName: file.name, size: Math.ceil(file.size / 1000) })
- count--;
- if (count == 0) {
- uploadFormData(fmDataList).then(result => {
- if (result.fileNames) {
- this.imageList.forEach((t, index) => {
- t['fileName'] = result.fileNames[index]
- })
- }
- this.imageListStatus = true;
- })
- }
- }
- }
- }
- },
- },
- };
值得介绍的是上述代码中的封装好的 uploadFormData 方法,因为我们用原生的方式去上传,且上传的是一个二进制流的 formData ,所以我们就不能再用项目里面已经封装好的 request 去发起请求,而是要用 new XMLHttpRequest(),具体怎么实现,可以看下面实现步骤( 当然具体要看后端 API 怎么去做),以下面为例:
封装 uploadFormData.js
- /**
- * @param {string} file base64格式的图片
- * @param {string} imgFormat 需要转化为 formData 的图片格式
- */
- var uploadImgUrl = process.env.VUE_APP_BASE_API + "/common/upload"; // 上传的图片服务器地址
- import data2blob from "@/lib/data2blob.js";
- import mimes from "@/lib/mimes.js";
- import { getToken } from "@/utils/auth";
-
-
- export default function (files, imgFormat = 'png') {
- // files 就是上面 fmDataList 里的 base64 数组
- const allowImgFormat = ["jpg", "png"];
- const format = allowImgFormat.indexOf(imgFormat) > -1 ? 'jpg' : imgFormat;
- const fmData = new FormData();
- if (Array.isArray(files) && files.length > 0) {
- files.forEach(t =>
- // data2blob() 方法是通过 Blob 转化为二进制流
- fmData.append('file', data2blob(t.file, mimes[format]))
- )
- } else {
- fmData.append('file', data2blob(files, mimes[format]))
- }
- // XMLHttpRequest 去做具体请求
- return new Promise((resolve, reject) => {
- let client = new XMLHttpRequest();
- client.open("POST", uploadImgUrl, true);
- client.withCredentials = false; // 是否支持跨域
- client.onreadystatechange = function () {
- if (this.readyState !== 4) {
- return;
- }
- if (this.status === 200 || this.status === 201) {
- resolve(JSON.parse(this.responseText));
- } else {
- reject(this.status);
- }
- };
- client.setRequestHeader("Authorization", "Bearer " + getToken());
- client.send(fmData);
- })
- }
base64 转 二进制 data2blob.js
- /**
- * database64文件格式转换为2进制
- *
- * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
- * @param {[String]} mime [description]
- * @return {[blob]} [description]
- */
- export default function(data, mime) {
- data = data.split(',')[1];
- data = window.atob(data);
- var ia = new Uint8Array(data.length);
- for (var i = 0; i < data.length; i++) {
- ia[i] = data.charCodeAt(i);
- };
-
- return new Blob([ia], {
- type: mime
- });
- };
文件格式 mimes.js
- export default {
- 'jpg': 'image/jpeg',
- 'png': 'image/png',
- 'gif': 'image/gif',
- 'svg': 'image/svg+xml',
- 'psd': 'image/photoshop'
- };
边记录边学习边成长,加油加油加油~~~~