鲁迅说: 要用promise封装一个文件选择器

156 阅读2分钟

需求

1,通过promise封装

2,文件类型和大小校验不通过时reject报错信息

3,可以多选文件

上代码
js版
/**
 * @param fileTypeList 文件后缀列表,如['.png','.jpg']
 * @param selectNum 选择数量 1和0为单选 其余多选
 * @param size 文件大小限制 5m的话为 5*1025*1024
 * @returns
 */
export const openChoiceFile = (fileTypeList, selectNum, size) => {
  return new Promise((resolve, reject) => {
    let input = document.createElement('input');
    input.type = 'file';
    input.accept = fileTypeList.join(',');
    //判断是否多选
    input.multiple = !(!selectNum || selectNum === 1);
    input.onchange = (e) => {
      const fileListObj = e.target.files;
      //校验选择文件数量
      if (fileListObj.length > selectNum) {
        reject(`只能上传${selectNum}份文件`);
        // 释放dom,避免内存泄漏
        return (input = undefined);
      }
      const fileList = [];
      //循环FileLists实例,FileLists不是数组只能用普通for循环
      for (let i = 0; i < fileListObj.length; i++) {
        const file = fileListObj.item(i);
        //如果size有传,校验文件大小
        if (size && file.size > size) {
          reject(new Error(`文件过大,不可超过${(size / 1048576).toFixed(2)}M`));
          // 释放dom,避免内存泄漏
          return (input = undefined);
        }
        //校验文件类型
        const fileType = file.name.slice(file.name.lastIndexOf('.'));
        if (!fileTypeList.includes(fileType)) {
          reject(new Error('文件类型错误'));
          // 释放dom,避免内存泄漏
          return (input = undefined);
        }
        fileList.push(file);
      }
      // 输出文件列表
      resolve(fileList);
      input = undefined; // 释放dom,避免内存泄漏
    };
    input.click();
  });
};
ts版
/**
 * @param fileTypeList 文件后缀列表,如['.png','.jpg']
 * @param selectNum 选择数量 1和0为单选 其余多选
 * @param size 文件大小限制 5m的话为 5*1025*1024
 * @returns
 */
export const openChoiceFile = (
  fileTypeList: string[],
  selectNum: number,
  size?: number
) => {
  return new Promise<File[]>((resolve, reject) => {
    let input: HTMLInputElement | undefined = document.createElement('input');
    input.type = 'file';
    input.accept = fileTypeList.join(',');
    //判断是否多选
    input.multiple = !(!selectNum || selectNum === 1);
    input.onchange = (e: any) => {
      const fileListObj = e.target.files;
      //校验选择文件数量
      if (fileListObj.length > selectNum) {
        reject(`只能上传${selectNum}份文件`);
        // 释放dom,避免内存泄漏
        return (input = undefined);
      }
      const fileList: File[] = [];
      //循环FileLists实例,FileLists不是数组只能用普通for循环
      for (let i = 0; i < fileListObj.length; i++) {
        const file = fileListObj.item(i);
        //如果size有传,校验文件大小
        if (size && file.size > size) {
          reject(new Error(`文件过大,不可超过${(size / 1048576).toFixed(2)}M`));
          // 释放dom,避免内存泄漏
          return (input = undefined);
        }
        //校验文件类型
        const fileType = file.name.slice(file.name.lastIndexOf('.'));
        if (!fileTypeList.includes(fileType)) {
          reject(new Error('文件类型错误'));
          // 释放dom,避免内存泄漏
          return (input = undefined);
        }
        fileList.push(file);
      }
      // 输出文件列表
      resolve(fileList);
      input = undefined; // 释放dom,避免内存泄漏
    };
    input.click();
  });
};

使用方式:

//选择单个文件
const selectOne = async () => {
  try {
    //解构出单个文件
    const [file] = await openChoiceFile(['.jpg', '.png'], 1, 5 * 1024 * 1024);
    //打印单个文件
    console.log(file);
  } catch (err) {
    //获取校验失败信息,提示用户
    console.log(err);
  }
};
​
//选择多个文件
const selectFive = async () => {
  try {
    const fileList = await openChoiceFile(['.jpg', '.png'], 5, 5 * 1024 * 1024);
    //打印文件列表
    console.log(fileList);
  } catch (err) {
    //获取校验失败信息,提示用户
    console.log(err);
  }
};