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;
  });
};
相关推荐
二十雨辰1 小时前
[CSS3]Flex布局
前端·html·css3
小镇学者1 小时前
【JS】Vue 3中ref与reactive的核心区别及使用场景
前端·javascript·vue.js
xosg1 小时前
HTMLUnknownElement的使用
java·前端·javascript
xosg1 小时前
如何选用正确的html元素
前端·html
周之鸥1 小时前
html主题切换小demo
前端·html
Code_Geo2 小时前
python中Web框架Flask vs FastAPI 对比分析
前端·python·flask
打小就很皮...2 小时前
深入探索 CSS 中的伪类:从基础到实战
前端·css
Lhuu(重开版3 小时前
Vue:axios(GET请求)
前端·javascript·vue.js
万物更新_3 小时前
HTML-前端
前端·html
sg_knight3 小时前
Flutter跨平台通信实战|3步打通Android原生能力,实现底层API调用!
android·前端·javascript·flutter·跨平台·web·双向通信