基础知识之上传与下载

发布时间 2023-12-15 20:00:09作者: 乐盘游

缘起

上传和下载是两个经典场景,做项目的时候遇到了这两种情况。

上传

设置上传按钮

const upoadImgCom = () => {
  return (
    <>
      <label className={styles["upload-button"]} htmlFor="fileInputCompanyLogo">
        +
      </label>
      <input
        id="fileInputCompanyLogo"
        accept={acceptFileTypeList}
        onChange={uploadFileAction}
        type="file"
        multiple
      />
    </>
  );
};

相应样式文件

.upload-button {
  color: #c3c3c3;
  background: #f2f2f2;
  width: 100px;
  display: flex;
  height: 40px;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  &:hover {
    color: #008cff;
    border: 1px solid #008cff;
  }
}
input {
  display: none;
}

获取上传文件

注意这里获取到的uploadFile.files是文件流类型,不能用数组方法。[...uploadFileEle.files]转化为数组类型后,判断是否通过上传校验(大小、文件类型等)

const uploadFileAction = (e) => {
  const uploadFileEle = document.querySelector("#fileInputCompanyLogo");
  if (!uploadFileEle.files.length) return;
  // 校验上传文件是否符合准入条件
  const files = [...uploadFileEle.files];
  const isValid = beforeUpload(files, fileSizeLimit, legTypeList);
  if (!isValid) return;

  const file = uploadFileEle.files[0];
  dispatch({ type: "uploadImg", payload: file });
  // onchange监听的为input的value值,只有再内容发生改变的时候去触发
  // 成功后将value值置空,使得能够检测到下一次上传内容
  e.target.value = "";
  // }
};

关于beforeUpload方法

以上传 20MB,类型为图片文件为例

// 文件大小限制
const fileSizeLimit = 20 * 1024 * 1024;
// 文件类型限制
const legTypeList = ["png", "jpg", "jpeg"];

// 1 KB = 1024 B
// 转换文件大小为可读单位
export const getFileSize = (fileSize) => {
  let result = "";

  if (fileSize >= 1073741824) {
    // B => GB
    result =
      fileSize % 1073741824 === 0
        ? `${fileSize / 1073741824}G`
        : `${Math.trunc(fileSize / 1073741824)}G`;
  } else if (fileSize >= 1048576) {
    // B => MB
    result =
      fileSize % 1048576 === 0
        ? `${fileSize / 1048576}MB`
        : `${Math.trunc(fileSize / 1048576)}MB`;
  } else if (fileSize >= 1024) {
    // B => KB
    result =
      fileSize % 1024 === 0
        ? `${fileSize / 1024}KB`
        : `${Math.trunc(fileSize / 1024)}KB`;
  } else if (fileSize !== undefined) {
    result = `${fileSize}B`;
  }
  return result;
};

// 根据文件名后缀获取当前文件类型
export const getLastFileTypeByName = (value) => {
  const dotArray = value.split(".");
  const lastDotArray = dotArray[dotArray.length - 1];
  return lastDotArray;
};

export const getFileTypeBySource = (file, legitTypeList) => {
  // 注意这里的类型判断一定要从文件名读取! ios 无 file.type!!!
  const exactFileValue = file.name;
  const lastFileTypeByName = getLastFileTypeByName(exactFileValue);

  // 实测移动端 不走相册 直接选图片文件 尾缀为大写PNG 这里多加一层逻辑
  // ↑ 从H5抄过来的 问题不大
  // 这里全部转换成小写 进入匹配
  let isLegitType = false;
  isLegitType = legitTypeList.some((key) =>
    lastFileTypeByName.toLowerCase().includes(key)
  );
  return isLegitType;
};

// 上传文件前置校验 文件类型 文件大小
export const beforeUpload = (files, fileSizeLimit, legTypeList) => {
  const readableMaxFileSize = getFileSize(fileSizeLimit);
  const fileSupportTypeText = legTypeList.join("、");

  // if (!(files && files.length)) {
  //   message.error(`请添加${kind}`);
  //   return false;
  // }

  let flag = true;

  // eslint-disable-next-line consistent-return
  files.forEach((file) => {
    const isLegFileType = getFileTypeBySource(file, legTypeList);

    if (!isLegFileType) {
      message.error(`文件支持格式: ${fileSupportTypeText}`);
      flag = false;
    }
    if (file.size > fileSizeLimit) {
      message.error(`文件最大支持${readableMaxFileSize}`);
      flag = false;
    }
  });
  return flag;
};

上传图片后可临时预览

https://juejin.cn/post/7240486780578480189

FileReader 转换为 base64 格式

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片本地预览示例</title>
  </head>
  <body>
    <h3>阿宝哥:图片本地预览示例</h3>
    <input type="file" accept="image/*" onchange="loadFile(event)" />
    <img id="previewContainer" />

    <script>
      const loadFile = function (event) {
        const reader = new FileReader();
        reader.onload = function () {
          const output = document.querySelector("#previewContainer");
          output.src = reader.result;
        };
        reader.readAsDataURL(event.target.files[0]);
      };
    </script>
  </body>
</html>


下载

图片、pdf、xml 的链接点击是预览
需要转换成 blob 文件流

五种方法 以及 对比
https://juejin.cn/post/6844904069958467592?searchId=20231215174642867C7A34315E52A747FB#heading-20

浏览器打开图片链接是直接预览还是下载

取决于图片链接的响应头 Content-Disposition 的属性
Content-Disposition 为 inline 在浏览器打开直接预览
Content-Disposition 为 attachment 在浏览器打开直接下载

链接:https://juejin.cn/post/7177346491059535932

值得一看的参考文章

前端二进制相关知识
https://juejin.cn/post/7262754051271032891?searchId=2023121416035302BFFAA3DF21DC00CAF9