首页>>前端>>Vue->在已有异步接口且每次只能处理一个文件的前提下,一种前端批量导入的处理方式

在已有异步接口且每次只能处理一个文件的前提下,一种前端批量导入的处理方式

时间:2023-11-30 本站 点击:3

在实际开发中,经常会遇到一些文件上传处理,但往往还会伴随一些上传后的异步处理以及异步条件下批量处理。在解决这种矛盾事件时,会有点头痛。

问题描述

本人在开发时有一个需求,实现上传图片,提取图片信息,并自动推送图片信息到数据库,这本来并不复杂,但是加了前提条件,就有些头痛了。现在的情况是,提取图片并推送图片已有现成的接口(不可以更改接口,这是一个公用的接口,不会因为需求随便变动),但是该接口每次只能处理一张图片,且提取完成后接口会自动推送图片信息到数据库,前端只需在上传成功后刷新页面即可。但问题是,本来接口提取的成功率就不是很大,现在需求方又要求必须支持批量导入,其实导入方面也是没什么问题,问题就在每次导入成功或失败后前端如何友好的提示,接口每完成一次前端提示一次,这明显使用起来体验不是很好。

解决方案

现在的实现方式是,做一个列表,每次选完文件后,展示这个列表,并给每个文件一个loading状态,在相应的文件处理完后,更改其状态,在列表中所有的文件都处理完后,通过Promise.all()在进行下一步处理,这样解决了前端频繁弹出提示框的问题。效果图类似如下(示意效果没有做过多的css处理):

实现这种效果很简单,知识点就一点就是 Promise.all()的应用。

实现方式(Vue)

首先通过input标签创建出类型为file的文件上传按钮

 <input type="file" multiple accept="image/*, .pdf" @change="getFileList"/>

multiple属性控制是否支持多选,accept控制文件的类型(image/* 所有的图片类型)change事件 其次展示列表 注:如果想使用谷歌的icon需要在最开始的index.html中引入,在main.js中引用也可以,需要export导出

<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

想实现loading动画请参考:css实现loading动画 列表:

<div class="listarea">      <!-- 通过for循环循环列表 -->      <template v-for="(item, i) in fileList">        <div class="filelist" :key="i">          <div class="file" style="width: 300px">            {{ item.name }}          </div>          <div class="file">{{ item.type }}</div>          <div class="file">{{ item.size }}</div>           <!-- 通过文件的状态判断显示,这里loading效果可以用文字表示,我上图的loading效果是通过css动画单独写的一个文件 -->          <div class="file">            <div v-if="item.status === 'loading'" class="loading-icon">              <div>                <loading-icon class="icon" />              </div>              <div style="width: 70px">                <span>正在上传</span>              </div>            </div>            <div              v-if="item.status === 'success'"              class="loading-icon"              style="color: #19be6b"            >              <div style="display: table; vertical-align: middle">                <i class="material-icons" style="font-size: 24px"                  >check_circle</i                >              </div>              <div style="width: 70px">                <span>上传成功</span>              </div>            </div>            <div              v-if="item.status === 'error'"              class="loading-icon"              style="color: #ed4014"            >              <div style="display: table; vertical-align: middle">                <i class="material-icons" style="font-size: 24px"                  >highlight_off</i                >              </div>              <div style="width: 70px">                <span>上传失败</span>              </div>            </div>          </div>          <div class="file" style="width: 300px">{{ item.message }}</div>        </div>      </template>    </div>

在js中主要两个函数一个是模拟后端异步接口处理函数,一个是change事件触发函数 异步接口:

 asyncFun(file, callback) {      console.log(file); // 假设对file文件处理      const n = Math.floor(Math.random() * 10 + 1); // 生成一个随机数      // 模拟异步      setTimeout(() => {        if (n % 2 === 0) {          const res = {            success: true,            message: "上传成功",          };          return callback(res);        } else {          const res = {            success: false,            message: "上传失败, 错误编码" + n,          };          return callback(res);        }      }, n * 1000);    },

change事件触发函数

getFileList(e) {      this.completeText = "";      this.fileList = [];      const files = e.target.files; // 获取文件列表      if (files.length) {        let promiseArr = [];        for (let file of files) {          const fileObj = {            name: file.name, // 文件name            type: file.type, // 文件类型            size: parseFloat(file.size / 1024 / 1024).toFixed(2) + "MB", // 文件大小            status: "loading", // 文件状态 人为赋值            message: "", // 报错信息          };          this.fileList.push(fileObj); // 展示列表          // 异步接收后端接口返回          let fileUpload = new Promise((resolve) => {            this.asyncFun(file, (res) => {              if (res.success) {                fileObj.status = "success"; // 更改文件上传后状态                fileObj.message = res.message;              } else {                fileObj.status = "error"; // 更改文件上传后状态                fileObj.message = res.message;              }              resolve(true);            });          });          promiseArr.push(fileUpload);        }        Promise.all(promiseArr).then(() => {          // 异步函数处理完下一步处理如:刷新列表          this.completeText = "上传全部完成";        });      }    },

完整代码:

<template>  <div class="main">    <div>      <input        type="file"        multiple        accept="image/*, .pdf"        @change="getFileList"      />      <span style="color: red">{{ completeText }}</span>    </div>    <div class="listarea">      <!-- <loading-icon class="icon" /> -->      <template v-for="(item, i) in fileList">        <div class="filelist" :key="i">          <div class="file" style="width: 300px">            {{ item.name }}          </div>          <div class="file">{{ item.type }}</div>          <div class="file">{{ item.size }}</div>          <div class="file">            <div v-if="item.status === 'loading'" class="loading-icon">              <div>                <loading-icon class="icon" />              </div>              <div style="width: 70px">                <span>正在上传</span>              </div>            </div>            <div              v-if="item.status === 'success'"              class="loading-icon"              style="color: #19be6b"            >              <div style="display: table; vertical-align: middle">                <i class="material-icons" style="font-size: 24px"                  >check_circle</i                >              </div>              <div style="width: 70px">                <span>上传成功</span>              </div>            </div>            <div              v-if="item.status === 'error'"              class="loading-icon"              style="color: #ed4014"            >              <div style="display: table; vertical-align: middle">                <i class="material-icons" style="font-size: 24px"                  >highlight_off</i                >              </div>              <div style="width: 70px">                <span>上传失败</span>              </div>            </div>          </div>          <div class="file" style="width: 300px">{{ item.message }}</div>        </div>      </template>    </div>  </div></template><script>import loadingIcon from "./loadingIcon.vue";export default {  components: { loadingIcon },  name: "FileUpload",  data() {    return {      fileList: [],      completeText: "",    };  },  methods: {    getFileList(e) {      this.completeText = "";      this.fileList = [];      const files = e.target.files;      if (files.length) {        let promiseArr = [];        for (let file of files) {          const fileObj = {            name: file.name,            type: file.type,            size: parseFloat(file.size / 1024 / 1024).toFixed(2) + "MB",            status: "loading",            message: "",          };          this.fileList.push(fileObj);          let fileUpload = new Promise((resolve) => {            this.asyncFun(file, (res) => {              console.log("res", res);              if (res.success) {                fileObj.status = "success";                fileObj.message = res.message;              } else {                fileObj.status = "error";                fileObj.message = res.message;              }              resolve(true);            });          });          promiseArr.push(fileUpload);        }        Promise.all(promiseArr).then(() => {          this.completeText = "上传全部完成";        });      }    },    asyncFun(file, callback) {      console.log(file);      const n = Math.floor(Math.random() * 10 + 1);      setTimeout(() => {        if (n % 2 === 0) {          const res = {            success: true,            message: "上传成功",          };          return callback(res);        } else {          const res = {            success: false,            message: "上传失败, 错误编码" + n,          };          return callback(res);        }      }, n * 1000);    },  },};</script><style scoped>.main {  width: 1000px;}.listarea {  margin: 8px;  width: 100%;  height: 500px;  border: 1px solid #ccc;  border-radius: 8px;  overflow-y: auto;}.filelist {  display: flex;  justify-content: space-between;  text-align: left;}.filelist .file {  margin: 8px;  padding: 8px;}.loading-icon {  display: flex;  justify-content: space-between;}.icon {  width: 20px;  height: 20px;  display: inline-block;}</style>
原文:https://juejin.cn/post/7098264067046899743


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3810.html