文件上传,给老夫爬

前言

本文主要是对文件上传,下载,导出等常用的功能进行了描述,借用了 ant-design-vue(antdv) 组件库中的 upload 来实现(经观察 element-ui ,view-design 同样适用,属性和方法基本一致)。如果遇到类似文件上传开发需求或不太熟悉文件上传的同学,可以放心食用。

上传文件

需求:文件多选,前端进行文件解析名单,计算总数和重复数等,仅支持xlsx、csv格式,上传后需要能够预览或者下载,名单上传给后端的数据要求是字符串文本,而不是文件,即”chinidenaiyou@qq.com\nchinidenaiyou@qq.com\n”。注意:\n用于换行

.xlsx上传

template部分
文件上传,给老夫爬

<a-upload-dragger
  v-model:fileList="fileList"
  accept=".xlsx, .csv"   // 支持啥后缀写啥后缀,','用来隔开
  :beforeUpload="beforeFileUpload"
  @change="handleChange"
>
    --框内的,爱放啥放啥  --
</a-upload-dragger>

处理表格信息

先借助 xlsx 第三方依赖库把文件转化成数组

// 1.js
const reader = new FileReader();
reader.readAsBinaryString(item.originFileObj);

reader.onload = (e: any) => {
  //  拿到文件的内容
  const data = e.target.result;
  // 以二进制格式读取文件信息,即设置type为binary1const workbook = XLSX.read(data, { type: "binary" });
  【2let sheetData = XLSX.utils
    .sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], {
      header: ["data"],
    })
    .map((t: any) => Object.values(t)[0]);
  resolve(sheetData);
};

【1】workbook 的部分信息

文件上传,给老夫爬

【2】通过 workbook 拿到所有的工作表,即 workbook.Sheets ,但通常我们拿的都是第一张表,即 workbook.SheetNames[0] ,因此通过 XLSX 把表格转化成了 json 对象,再通过 Object.values 把 json 对象转化成数组,方便后续计算长度数量。

文件上传,给老夫爬

文件上传,给老夫爬

注意:此时我们需要设置表头为”data”,如果不进行设置的话,会把第一行默认为表头,即会丢失第一行的数据.

文件上传,给老夫爬

但需要了解的是,onload 是异步任务,因此需要进行 promise 处理。
每个 promise 返回的都是一个数组,因此 promise.all 后的结果是个二维数组,借助 flat 方法进行扁平化。

// 2.js
let promises = fileList
        .map((item: any) => {
          return new Promise((resolve) => {
              ...上述代码,即1.js
              ...
              ...
          })
          
return Promise.all(promises).then((results) => {
    let sumArray = results.flat(); // 合并数组
    return sumArray;
});

获取到了最终的数组结果,就需要根据产品提出的需求进行相应的计算了

const readAndCombineFiles = (fileList: any) => {
    ... 2.js
}

const handleChange = (info) => {
     readAndCombineFiles(info.fileList).then((sumArray: any) => {
          let uniqueArray = [...new Set(sumArray)]; // 进行去重处理
          receiverCnt.value = sumArray.length; // 名单总人数
          uniqueCnt.value = uniqueArray.length; // 去重后的人数
          res = uniqueArray.join('\n') // 文本字符串,即最终的数据
    }
}

.csv 或者 .xlsx 文件下载

产品要求上传后需要有下载或者预览,所以在上传之前给文件加了url,使其能够下载。

const beforeFileUpload = (file: any) => {
  // 创建一个文件类型的blob对象
  const blob = new Blob([file], { type: file.type }); 

  // 创建一个本地临时的下载链接
  file.url = URL.createObjectURL(blob);
  return false;
};

html 文件上传

文件上传,给老夫爬

由于要求单选,所以 multiple 属性得设为 false ,但这只是针对选择文件时的控制(即不能按住 ctrl 进行多选),如果选完上一个再接着选择一个文件,展示的文件列表数据仍然是多个,所以需要对文件列表进行控制。

const handleChange = (info) => {
    // 拿到所有文件列表
    let resFileList = [...info.fileList];
    
    // 拿到最后即最新的文件列表
    resFileList = resFileList.slice(-1);
    
    // 把它赋值给antdv组件中的文件列表值
    fileList.value = resFileList;
    
    // 然后传给后端最新的文件,拿到token(保存的时候需要用到)
    const file = resFileList[0]?.originFileObj;
    postTemplate(file).then((resp: any) => {
      mailTemplate.value = resp.upload_key;
    });
}

然后再把文件以formData的形式传给后端

export function postTemplate(file: File) {
  const formdata = new FormData();
  formdata.append("file", file);
  return instance.post(`/apis/v1/upload/email`, formdata, {
    headers: {
      "Content-Type": "multipart/form-data;charset=UTF-8",
    },
  });
}

.html 预览

.csv 预览

下载文件

文件上传,给老夫爬

有上传的功能就必有下载的需求,点击下载后,后端不返回文件流,而是返回字符串信息,例如 html 文档的字符串,表格数据文本的字符串。我们需要做的就是把字符串信息转成相应的 blob 对象,再利用 a 标签创建一个临时的本地地址提供下载。
文件上传,给老夫爬

文件上传,给老夫爬

<a-form-item label="邮件内容" class="emailDetai-form-item">
    <a @click.prevent="downLoadFile(rowDetail?.mailTemplate, 'template')">
        {{str.substring(str.length - 15)}}
      <DownloadOutlined/>
    </a>
</a-form-item>

.html文件的下载

文件上传,给老夫爬

path: 该路径表示的是存放在阿里云服务器中的文件路径

其中url为路径,tp表示下载的类型
  if (tp === "template") {
    // 获取到相应的字符串信息后进行处理
    dowanload({ path: url, tp }).then((resp: any) => {
      // 将字符串内容保存为Blob对象
      // resp.data为html文档字符串
      const blob = new Blob([resp.data], {
        type: "text/html;charset=utf-8;",
      });

      // 创建一个下载链接
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      
      // 设置文件名,要求保留文件名保留10位数
      link.download = url.substring(url.length - 15);

      // 将链接添加到DOM中,模拟点击触发下载
      document.body.appendChild(link);
      link.click();

      // 清理临时链接
      document.body.removeChild(link);
    });
  }

.csv文件的下载

文件上传,给老夫爬

else {
    dowanload({ path: url, tp }).then((resp: any) => {
      // 将字符串内容保存为Blob对象
      // resp.data为字符串文本,带换行的那种,即'\n'
      const blob = new Blob([resp.data], {
        type: "text/csv;charset=utf-8;",
      });

     // 后续代码同上,即一样创建a标签进行下载
    });
  }

编辑时文件上传的页面

在页面初始的时候发送请求拿到数据,然后把该数据转化成本地的下载链接,对组件库的列表进行赋值,其中 name 为文件名,status 为上传的状态,url 为下载的地址,也就是主动创建了一个初始化的文件列表。
文件上传,给老夫爬

dowanload({ path: value, tp: "receive" }).then((resp: any) => {
  // 将字符串内容保存为Blob对象
  const blob = new Blob([resp.data], {
    type: "text/csv;charset=utf-8;",
  });

  fileList.value = [
    {
      uid: "-1",
      name: str.substring(str.length - 15),
      status: "done",
      url: URL.createObjectURL(blob),
    },
  ];
});

导出文件

其实和文件下载一样,都是后端直接返回,不过由于是列表数据,我们需要将数据转成表格,再进行 a 标签下载。

文件上传,给老夫爬

const exportToExcel = () => {
      // 定义中文表头1const header = {
        receiver: "接收者",
        campaign_id: "活动ID",
        unsubscribe_time: "取消订阅时间",
      };
      
 【2const ws = XLSX.utils.json_to_sheet([header, ...resData.value], {
        skipHeader: true,
      });
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
      const blob = XLSX.write(wb, {
        bookType: "xlsx", // 工作簿类型
        bookSST: true,
        type: "array",  // 工作簿数据类型,即json_to_sheet中的第一个参数
      });

      const link = document.createElement("a");
      link.href = URL.createObjectURL(new Blob([blob]));
      link.download = "取消订阅列表数据.xlsx";
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
}

【1】header 为创建表格的表头,自定义的中文表头代替数据中的 key ,即 key:”自定义的中文表头”,如果不设置表头,会默认的以数据中的 key 为表头,并且表头设置了几个就会展示几列数据。

文件上传,给老夫爬

【2】通过 XLSX 库中的 json_to_sheet 方法,把 json 数据转化成表格,第一个参数 [header, …resData.value] 中数组第一个参数 header 为自定义的表头,而数组第二个参数为后端返回的数据,需要展开。第二个参数中的 skipHeader 为是否跳过默认的表头,如果不设置的话,则会由默认的表头,如下图所示。

文件上传,给老夫爬

book_new 方法为创建一个新的工作簿,book_append_sheet 则是把新的工作簿,数据,以及工作簿的名字添加到 sheet 表格中,然后把有数据的工作簿创建成一个 blob 对象,再利用a标签进行下载。

遇到的问题

取消 andv 组件库中 action 属性默认发送上传请求

由于产品设计要求,因此想单纯的利用了 antdv 的文件上传组件,但发现,不管你设不设置 action 属性,它都会文件上传发送请求,区别在于本地还是你设置的

这里我没有设置 action 属性或者 action 属性为空,它却自发的发送了请求。

文件上传,给老夫爬

关键在于你发送请求还是勉强可以接受,但上传文件后一直报红,就很难受,因为根本不需要上传后的状态

文件上传,给老夫爬

并且,此时使用 antdv 上传组件,会发现 change 事件触发了三次。(上传中、完成、失败都会调用这个函数。)

解决办法: 在文件上传前返回false,停止文件上传的请求

const beforeFileUpload = (file) => {
    return false;
}

原文链接:https://juejin.cn/post/7326578130180522018 作者:吃腻的奶油

(0)
上一篇 2024年1月22日 下午4:10
下一篇 2024年1月22日 下午4:21

相关推荐

发表回复

登录后才能评论