uploadImage公共方法封装

简单记录一下周五封装uploadImage方法时的一些思考

源码

typescript 复制代码
import { Message } from 'vue3组件库';
import to from 'await-to-js';
import axios from 'axios';
interface IOption {
  max?: number;
  validator?: ((file: File) => boolean) | ((file: File) => Promise<boolean>);
}
​
export async function uploadIcon(file: File) {
  // 请求体参数使用FormData对象代替普通对象
  const params = new FormData();
  params.append('file', file);
  return axios({
    headers: {
      'Content-Type': undefined,
    },
    method: 'post',
    url: 'xxx/upload/image', // 图片上传接口
    data: params,
  });
}
​
/**
 * @param file  图片文件对象
 * @param option 上传配置对象 max为限制文件大小(单位KB) validator为自定义校验函数
 * @returns Promise<url>
 */
export async function uploadImage(file: File, option: IOption = {}) {
  const { max, validator } = option;
  // 如果有max限制就进行文件大小校验
  if (max && file.size > max * 1024) {
    return Promise.reject(
      Message({
        message: `图片超过${max}KB的限制大小, 请压缩图片大小`,
        type: 'info',
      })
    );
  }
​
  // 如果没有validator 或者 有validator且await validator的结果为true,那么进行图片上传
  if (!validator || (await validator(file))) {
    const [error, res] = await to(uploadIcon(file));
    if (error) {
      return Promise.reject(
        Message({
          message: error.message || '图片上传出错',
          type: 'error',
        })
      );
    }
    Message({ message: '上传成功', type: 'success' });
    return res.data.data;
  } else {
    return Promise.reject(
      Message({
        message: '图片未通过校验',
        type: 'info',
      })
    );
  }
}

使用示例

typescript 复制代码
const [error, url] = await to(uploadImage(file, { max: 500, validator }));
if (!error) {
  // 消费url
}

为什么使用FormData对象

FormData 对象主要用于发送表单数据,特别是包含文件上传请求的情况。它有以下几个主要优点:

  1. 可以发送键值对数据,同时也支持文件上传。普通对象无法直接发送文件数据(必要性)
  2. 当发送大量数据时,使用 FormData 可以使数据在传输过程中保持较小的大小,因为它的数据格式比普通的 JSON 对象更紧凑 (请求体积小,性能优)
  3. FormData 对象与 XMLHttpRequestfetch API 都有很好的兼容性,可以方便地发送异步请求。
  4. FormData 对象自动将请求头的 Content-Type 字段设置为 multipart/form-data,并生成适当的 boundary参数,这对于文件上传是必需的 (包含文件数据的http请求有格式的要求,即content-type字段与boundary参数,使用FormData对象作为请求体,可以自动设置符合文件上传要求的content-type与boundary)

因此,当你需要在前端发送包含文件的 POST 请求时,使用 FormData 对象是一个很好的选择。

结论

如果我们的请求体对象中有文件对象,那么需要用FormData对象来代替普通对象

为什么设置Content-type为undefined

在使用 axios 发送 POST 请求上传文件时,如果我们设置 Content-Typeundefined,那么浏览器会自动将 Content-Type 设置为 multipart/form-data,并且会自动添加一个合适的 boundary 参数。

multipart/form-data 是一种编码类型,它允许在一个 POST 请求中发送多部分数据,这对于发送包含文件的表单数据非常有用。boundary 参数用于定义请求中各个部分的边界。

如果我们手动设置 Content-Typemultipart/form-data,但没有添加 boundary 参数,那么请求可能会失败,因为服务器可能无法正确解析请求体。因此,当我们需要发送 multipart/form-data 类型的 POST 请求时,最好将 Content-Type 设置为 undefined,让浏览器自动处理。

结论

显示设置Content-type参数为undefined目的是让浏览器自动修正请求头里的content-type字段值和boundary参数。其实使用FormData对象作为请求体已经将http请求的content-type与boundary参数设置好了,算是双重保险吧

plus:Content-type: undefined可以省略,但是使用FormData对象承载文件对象是必须的。

如何支持 同步/异步validator

同步validator就不用说了,直接用if判断其结果是否为true即可,如果用户传入的校验函数为异步方法呢,也很简单,await validator即可对同步或者异步的validator方法进行统一处理。(await后面的值为非Promise对象时会用Promise.resolve方法对其包装)

如何支持消费组件中await-to-js的错误感知

uploadImage上传失败时,return Promise.reject返回一个拒绝的Promise对象即可。

如何通过自定义validator限制图片宽高一致

思路是validator函数中,通过创建一个Image对象image,并通过URL.createObjectURL(file)构造其url,在imageonload回调函数中通过image.widthimage.height拿到图片宽高。

ts 复制代码
// 图片上传相关回调
const validator = (file: File) => {
  return new Promise<boolean>((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const image = new Image();
    image.onload = function () {
      if (image.width !== image.height) {
        Message({
          message: '图片分辨率宽高不一致, 请设置相同的图片宽高',
          type: 'info',
        });
        reject(false);
      }
      resolve(true);
    };
    image.src = url;
  });
};
相关推荐
小小愿望5 分钟前
彻底禁用移动端H5页面默认下拉刷新功能:原理与实战
前端
Antioper13 分钟前
盘点前端经典手写代码题
前端
断竿散人13 分钟前
⚡CSS动画性能优化:60fps丝滑体验的终极秘籍
前端·css·性能优化
天天摸鱼的java工程师18 分钟前
前端难还是后端难?作为八年后端开发,我想说点实话
前端·后端·程序员
工呈士36 分钟前
TCP/IP 协议详解
前端·后端·面试
AryaNimbus41 分钟前
“我 Cursor Pro 怎么用三天就没了?”——500 次额度的真相是这样
前端·cursor
Turing_01042 分钟前
《HarmonyOS Next分布式实战:跨设备流转玩出花!迁移+协同=超级终端》
前端
前端小巷子44 分钟前
跨标签页通信(二):Service Worker
前端·面试·浏览器
默默地离开1 小时前
文档流之css布局块(盒子模型)
前端·css
派大星jason1 小时前
flutter 上架国内应用市场
前端