axios二次封装 大文件分片上传的封装

实现功能:

  1. 取消掉重复的请求
  2. 路由跳转可以取消上个页面没发送的请求
  3. 统一请求方式 post get put delect格式统一
  4. 封装上传下载
  5. 统一提示实现(提供配置自定义提示)
  6. 非200状态码 错误信息只提示一次
  7. 大文件分片上传的封装,某一片上传失败 自动上传2次

用法

import { axios } from '@/common/request/index'
// noMessage 是否提示
axios.post('/proxyapi/api/sys/auth/login', data, { headers: { Authorization: ''}, noMessage: true })
axios.get('/proxyapi/api/sys/auth/login-user', data, { headers: { Authorization: ''} })
// 上传 不需要加请求头配置已经自己加了 不需要formData 配置自己加了
// chuckSize有的话就是分片上传 没有的话就是普通一次上传
// chuckSize值的单位是字节(一次上传多大)
axios.requestFormData('url', {file:file},{chuckSize:300})
// 下载
axios.requestExcelStream('url', {})

代码封装

import axios from 'axios'
import qs from 'qs'
import { router } from "@/router/index";
import { message } from "ant-design-vue";
import { store } from '@/store/index'

class request {
  http = {}
  pending = new Map()
  errorStatus = {
    '400': (Message = '系统错误') => { message.error(Message); },
    '401': (Message = '登录过期', router) => {
      message.error(Message);
      const timeout = setTimeout(() => {
        clearTimeout(timeout);
        store.commit({ type: 'logOut/logOut' })
        router.replace({ name: "login" });
      }, 500);
    },
    '403': (Message = '没有权限') => {
      message.error(Message)
      const timeout = setTimeout(() => {
        clearTimeout(timeout);
        router.replace({ path: "/error", query: { title: '没有权限', status: 403 } });
      }, 500);
    },
    '404': (Message = '地址错误') => { message.error(Message) },
    '500': (Message = '系统错误') => { message.error(Message) },
    '504': (Message = '网关超时') => { message.error(Message) },
  }
  constructor(baseURL = '', timeout = 10000, token, header = 'application/json;charset=UTF-8') {
    if (!request.instance) {
      request.instance = this
      this.pending = new Map(); // 正在进行的请求
      this.init(baseURL, timeout, token, header)
    }
    return request.instance
  }
  // 处理pending的url
  dealUrl(config = {}, type = 'request') {
    const method = config.method.toLocaleLowerCase(),
      url = config.url
    // 请求的config拼装(pending的key)
    if (type === 'request') {
      if (['get', 'delect'].includes(method)) return `${url}?params=${JSON.stringify(config?.params)}?method=${method}`
      if (['post', 'put'].includes(method)) return `${url}?data=${JSON.stringify(config?.data)}?method=${method}`
      return ''
    }
    // 响应的config拼装(pending的key)
    if (['get', 'delect'].includes(method)) return `${url}?params=${JSON.stringify(config?.params)}?method=${method}`
    if (['post', 'put'].includes(method)) return `${url}?data=${JSON.stringify(JSON.parse(config?.data))}?method=${method}`
    return ''
  }
  // 添加peding
  addPending(config = {}) {
    config.cancelToken = new axios.CancelToken(cancle => {
      const url = this.dealUrl(config, 'request')
      this.pending.set(url, cancle)
    })
  }
  // 取消pending
  canclePending(config = {}, type = 'request') {
    const url = this.dealUrl(config, type)
    // 去寻找pending里是否有和本次相同的请求 有的话就取消上一次请求 并且删除pending的重复记录
    if (!this.pending.has(url)) return
    // 取消上次请求
    this.pending.get(url)?.()
    // 删除上次记录
    this.pending.delete(url)
  }
  init(baseURL, timeout, token, header) {
    // 创建一个axios实例
    this.http = axios.create({
      baseURL,
      timeout,
      headers: {
        'Content-type': header,
        Authorization: token, // token
      }
    });
    this.request()
    this.response()
  }
  // 添加请求拦截器
  request() {
    // 添加请求拦截器
    this.http.interceptors.request.use(config => {
      debugger
      if (!config.headers.Authorization) {
        config.headers.Authorization = store.getters['user/getToken'] ?? ''
      }
      if (config?.ifAddPending) {
        // 取消重复请求
        this.canclePending(config)
        // 添加pending
        this.addPending(config)
      }
      // 去除请求参数中的前后空格
      const params = config?.data ?? {}
      for (let key in params) {
        if (params[key] && typeof params[key] === 'string') params[key] = params[key]?.trim()
      }
      // 普通表单形式(键值对)
      if (config?.headers?.['Content-type'] === 'application/x-www-form-urlencoded') {
        config.data = qs.stringify(config.data)
      }
      // // formData格式
      // if (config?.headers?.['Content-type'] === 'multipart/form-data') {
      //   const params = { ...config.data }
      //   const formData = new FormData()
      //   for (let key in params) {
      //     formData.append(key, params[key])
      //   }
      //   config.data = formData
      // }
      return config
    }, error => {
      return Promise.reject(error);
    });
  }
  // 添加响应拦截器
  response() {
    this.http.interceptors.response.use(response => {
      const { statusText, data, config } = response
      if (config?.ifAddPending) {
        // 取消pending
        this.canclePending(config, 'response')
      }
      if (statusText === "OK") {
        if (!config?.noMessage) {
          if (!data?.Success) message.error(data?.Message)
          if (data?.Success) message.success(data?.Message)
        }
        return data
      }
      return {}
    }, error => {
      // 取消pending
      for (let cb of this.pending.values()) {
        cb?.()
      }
      this.pending?.clear()
      if (error?.code === 'ERR_CANCELED') return
      if (!error?.response) return message.error('服务器断开连接');
      const {
        data: { Success = true, Code, Message },
        status
      } = error?.response;
      if (!error?.config?.noMessage) this.errorStatus?.[status + '']?.(Message, router)
      return Promise.reject(error?.response ?? {});
    });
  }
}

export default request



import request from "./axios";
import { message } from "ant-design-vue";
import { store } from '@/store/index'
// import { watch } from 'vue'

let instance = new request('', 5000, store.getters['user/getToken'], 'application/json;charset=UTF-8')
// watch(() => store.getters['user/getToken'], (token) => {
//   instance = new request('', 5000, token, 'application/json;charset=UTF-8')
// })

export class axios {
  // 暴露出去方便 1. 切换导航时取消掉未结束的请求 2. 添加全局loading防止请求未完成时切换导航 (任选一种)
  static pending() {
    return instance.pending;
  }
  static async commonFnData(url = '', data, config = {}, method) {
    if (!url || config?.constructor?.name !== 'Object') {
      message.error("请输入正确参数");
      return Promise.reject({ Success: false })
    }
    try {
      const res = await instance.http({
        url,
        method,
        data,
        ...config
      })
      if (res?.Success) return Promise.resolve(res)
      return Promise.reject({ Success: false, error: res })
    } catch (error) {
      return Promise.reject({ Success: false, error })
    }
  }
  static async commonFnParams(url = '', params, config = {}, method) {
    if (!url || config?.constructor?.name !== 'Object') {
      message.error("请输入正确参数");
      return Promise.reject({ Success: false })
    }
    try {
      const res = await instance.http({
        url,
        method,
        params,
        ...config
      })
      if (res?.Success) return Promise.resolve(res)
      return Promise.reject({ Success: false, error: res })
    } catch (error) {
      return Promise.reject({ Success: false, error })
    }
  }
  static async post(url, data, config) {
    return axios.commonFnData(url, data, config, 'post')
  }
  static async put(url, data, config) {
    return axios.commonFnData(url, data, config, 'put')
  }
  static async get(url, params, config) {
    return axios.commonFnParams(url, params, config, 'get')
  }
  static async delect(url, params, config) {
    return axios.commonFnParams(url, params, config, 'delect')
  }
  // 上传
  static async requestFormData(url, data, config = {}, method = 'post') {
    const { name, size } = data?.file,
      { file } = data,
      { chuckSize } = config,
      params = { ...data }
    // 不需要分片上传
    if (!chuckSize || chuckSize > size) {
      // formData格式
      const formData = new FormData()
      for (let key in params) {
        formData.append(key, params[key])
      }
      return axios.commonFnData(url, formData, { ...config, headers: { 'Content-type': 'multipart/form-data' } }, method)
    }
    // 分片上传 大文件上传
    const chuckLen = Math.ceil(size / chuckSize)
    const uploadArr = Array.from({ length: chuckLen }).map((item, index) => {
      const result = file.slice(index * chuckSize, (index + 1) * chuckSize),
        blob = new Blob([result], { type: file?.type }),
        formData = new FormData()
      blob.name = file.name
      params.index = index
      for (let key in params) {
        if (key === 'file') formData.append('file', blob)
        else formData.append(key, params[key])
      }
      return () => axios.commonFnData(url, formData, { ...config, headers: { 'Content-type': 'multipart/form-data' } }, method)
    })
    const res = await Promise.allSettled(uploadArr?.map(item => item()))
    // 上传失败 重复上传2次
    res?.map((item, index) => {
      if (item?.status === 'rejected') {
        uploadArr[index]().catch(() => {
          uploadArr[index]().then(() => {
            res[index].status = 'fulfilled'
          })
        })
      }
    })
    return res
  }
  // 获取文件流
  static async requestExcelStream(url = '', params = {}, config = {}, method = 'get') {
    const res = await axios.commonFnParams(url, params, { responseType: 'blob', ...config }, method)
    if (res.status === 200) {
      var blob = res.data
      var reader = new FileReader()
      reader.readAsDataURL(blob) // 转换为base64,可以直接放入a表情href
      reader.onload = function (e) {
        // 转换完成,创建一个a标签用于下载
        var a = document.createElement('a')
        a.download = (params && params.getStreamPage || '表格') + Date.now()
        a.href = e.target.result
        a.click()
        a.remove();
      }
    }
  }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值