React 中封装一个 FileReader 组件

React 中封装一个 FileReader 组件

最近有一个业务需求需要在页面内实现一个文件上传的功能,本来想找第三方库实现的,后来发现 HTML5 中新提供了一个 FileReader API 就能实现功能,因此就想着试试看自己写一个组件。

API 介绍

FileReader 有 6 个事件,以下是 MDN 中的描述:

事件 描述
abort 当文件读取放弃时调用,如调用 FileReader.abort()
error 当文件读取出错时被调用
load 当文件成功被读取时调用
loadend 当文件读取结束时被调用
loadstart 当文件读取开始时被调用
progress 文件读取过程中随机调用 \newline ProgressEvent 本身也有三个属性可以用来展示当前加载状态:lengthComputable, loaded, 和 total

几个事件的触发都可以通过 addEventListener 或者直接绑定对应事件完成触发:

addEventListener("progress", (event) => {
    
    });

onprogress = (event) => {
    
    };

FileReader 中包含 3 个可读属性:

属性 描述
FileReader.error 一个用来描述文件读取时发生异常的 DOMException
FileReader.readyState 表示文件读取的状态 \newline 0 为实例已创建,但是还没开始读取 \newline 1 为已经开始读取 \newline 2 为操作已完成
FileReader.result 读完的文件,格式为对应的调用函数

然后这是读取文件的方法:

方法 参数 描述
abort none 中断读取 \newline 调用后 FileReader.readyState 会变成 2,即 DONE
readAsArrayBuffer file or blob 将文件读取为 ArrayBuffer,适合处理图片这样的文件 \newline 这是一个新一点的 API,所以不是所有的浏览器都会支持,向下兼容的 API 为 readAsBinaryString()
readAsDataURL file or blob 将文件读取为 DataURL(base64 encoded string)
readAsText file or blob, [encoding] 将文件读取为文本,这个是最常操作的部分了,encoding 默认为 UTF-8

了解了基础的 API,那么就能封装一个简易的文件上传了

React 代码

这里分为两个组件写,组件 1 就是渲染部分:

添加了 onclick 事件去重新设置 event.target.value 之后,onChange 能够获取事件的变化,否则没有办法使用该组件重复上传文件。

import {
    
     useState } from "react";
import {
    
     readUploadFile } from "./fileReaderHelper";
import "./styles.css";

export default function App() {
    
    
  const [img, setImg] = useState("");

  const changeHandler = async (e) => {
    
    
    const res = await readUploadFile(e);
    console.log(res);
    setImg(res);
  };

  return (
    <>
      <input
        type="file"
        onChange={
    
    changeHandler}
        onClick={
    
    (e) => (e.target.value = null)}
      />
      <p>Preview</p>
      <img src={
    
    img} alt="sample img" />
    </>
  );
}

组件 2 为 UploadHelper:

做一个完整的组件其实需求部分挺多的,比如说如果需求包含上传图片、文档、视频、音频等,这里可能需要使用正则,根据上传的文件去调用对应的 API。

export const readUploadFile = (e) => {
    
    
  const reader = new FileReader();

  return new Promise((res, rej) => {
    
    
    reader.addEventListener("error", () => {
    
    
      reader.abort();
      rej(new Error("Problem reading file"));
    });

    reader.addEventListener("progress", (e) => {
    
    
      console.log(e.loaded + " has been transferred");
      console.log("file size: " + e.total);
      console.log(e.lengthComputable);
    });

    reader.addEventListener("load", () => {
    
    
      res(reader.result);
    });

    reader.addEventListener("loadstart", () => {
    
    
      // add some spinner
    });

    reader.addEventListener("loadend", () => {
    
    
      // remove spinner
    });

    reader.readAsDataURL(e.target.files[0]);
  });
};

整体代码来说比较简单,因为 FileReader 是异步函数,因此在返回的时候需要包装成一个 Promise 去实现。另外 FileReader 在 Web Workers 中也有实现,因此也可以实现非阻塞式上传。

效果图:

before upload

after upload

猜你喜欢

转载自blog.csdn.net/weixin_42938619/article/details/125812453