编程技术文章分享与教程

网站首页 > 技术文章 正文

上传组件技术方案设计 文件上传组件

hmc789 2024-11-13 11:29:33 技术文章 1 ℃

需求背景

不管是什么类型的项目,文件上传都是一个常见的功能。因此,在项目中我们经常需要使用上传组件。通过直接使用组件,可以减少我们的开发量,本文主要介绍上传组件的开发方式。

模块设计

按照需求分析,我们至少需要实现一个基本的上传流程:点击按钮-用户选择文件-完成上传。当然,作为一个高可用的组件,它需要具备更多的能力。如下:

  • 支持上传文件列表的显示:包含文件名称、状态、删除按钮、上传进度,以及更多可能的功能支持
  • 可自定义模板:需要能够自定义上传组件的样式
  • 拖拽上传支持
  • 支持一系列生命周期钩子事件:beforeUpload、onProgress、onSuccess、onError、onChange
  • 自定义headers
  • 自定义file的表单名称
  • 自定义上传的数据
  • 可接受的文件类型
  • 限制图片规格

核心技术原理

上传文件有两种方式,传统模式使用form表单提交

<form method="post" action="#" enctype="multipart/form-data">
  <input type="file" />
  <button type="submit">Submit</button>
</form>

表单中enctype默认值为application/x-www-form-urlencoded,如果要有二进制数据需要设置为multipart/form-data。

另外,我们还可以使用ajax模式做异步提交。从input获得Files,涉及知识点:

  • 事件参数e.target.files是FileList对象,它是一个类数组对象,不是真正的数组
  • 通过files[索引]拿到对应的文件,它是一个File对象
  • FormData是针对XHR2设计的数据结构,我们可以使用它模拟HTML的<form/>

拖拽上传实现方式

如图所示,我们要实现的点有:

  1. dragover和dragleave添加或者删除对应的class
  2. drop事件拿到正在被拖拽的文件,删除class并且触发上传

需要注意拖拽上传对于组件来说是可选的,只有在属性drag为true的时候才会生效。

知识点:DataTransfer对象用于保存拖动并放下(drag and drop)过程中的数据,它可以保存一项或多项数据,这种数据项可以是一种或多种数据类型。

drop事件触发时,判断dataTransfer是否存在,存在则表示有对应的文件,通过e.dataTransfer.files获得File对象。

本地图片预览方式

本地图片预览可以在图片上传完成之前显示图片,利用两个API:

  1. URL.createObjectURL()
  2. FileReader.readAsDataURL()

两种方式主要区别:


FileReader.readAsDataURL(file)

URL.createObjectURL(file)

返回值

可以得到一段base64的字符串

可以得到当前文件的一个内存URl

执行机制

通过回调的形式返回,异步执行

直接返回,同步执行

内存清理

依照JS垃圾回收机制自动从内存中清理

存在于当前document内,清除方式只有unload()事件或revokeObjectURL()手动清除

如何选择:

  1. URL.createObjectURL(file)得到本地内存容器的URL地址,同步使用,比较方便快捷,多次使用需要注意手动释放内存的问题,性能优秀
  2. FileReader.readAsDataURL(file)胜在直接转为base64格式,可以直接用于业务,无需二次转换格式。

生命周期钩子实现

通过判断对应钩子事件是否存在,存在则执行

beforUpload && beforeUpload()

获取原始图片的大小

要限制图片的规格,我们需要在上传之前利用Image构造函数获取原始图片的宽高

export const getImageDimensions = (url: string | File) => {
  return new Promise<{ width: number; height: number }>((resolve, reject) => {
    const img = new Image();
    img.src = typeof url === "string" ? url : URL.createObjectURL(url);
    img.addEventListener("load", () => {
      const { naturalWidth: width, naturalHeight: height } = img;
      resolve({
        width,
        height,
      });
    });
    img.addEventListener("error", () => {
      reject(new Error("There was some proplem with the image."));
    });
  });
};

需要注意的是当我们传给函数的是一个File对象时,可以直接使用URL.createObjectURL(url)得到img的src。然后通过load事件监听获取原始宽高。

Tags:

标签列表
最新留言