如何处理大体积 XLSX/CSV/TXT 文件?

在开发过程中,可能会遇到这样的需求,我们需要从本地的 Excel 或 CSV 等文件中解析出信息,这些信息可能是考勤打卡记录,可能是日历信息,也可能是近期账单流水。但是它们共同的特点是数据多且繁杂,人工录入的工作量庞大容易出错,需要花费大量时间。那有没有什么方法能自动解析文件并获取有用信息呢?

1.png

当这个文件数据量也不是很多的时候,有很多前端工具可供选择。例如 SheetJS,就提供了从 Excel、CSV 中解析出用信息的很多方法,十分方便。

当数据量只是几千条的程度的,选择的余地很多,但是一旦数据量级增加,处理就变得复杂。如果 XLSX/CSV 数据量达到了 100w+ 条,Office、WPS 想打开看一下,都会需要很长的时间。

那又该如何从这样大体积的 Excel/CSV/TXT 中解析出数据呢?

 

背景

下面我们通过一个假设的需求,来讲述理解整个过程。假设我们需求是从本地 Excel、CSV、TXT(或者其他格式的)文件中解析出数据,并经过清洗后存入本地数据库文件中。但是这些文件体积可能是 5M、50M、500M 甚至更大。那么在浏览器环境下如何上传?Node 环境下应该如何解析?

首先,我们需要了解的是浏览器 Web 页面如何上传大体积文件?

 

Web 页面如何上传大体积文件?

Web 页面一般也是可以上传大文件的,但是会面临一个问题。如果要上传的数据比较大,那么整个上传过程会比较漫长,再加上上传过程的不确定因素,一旦失败,那整个上传就要从头再来,耗时

面对这个问题,我们可以通过将大文件分成多份小文件,每一次只上传一份的方法来解决。这样即使某个请求失败了,也无需从头开始,只要重新上传失败的那一份就好了。

如果想要使用这个方法,我们需要满足以下几项需求:

  • 大体积文件支持切片上传
  • 可以断点续传
  • 可以得知上传进度

首先看一下如何进行大文件切割。Web 页面基本都是通过 <input type=’file’ /> 来获取本地文件的。 而通过 input 的 event.target.files 获取到的 file,其实是一个 File 类的实例,是 Blob 类的子类。

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。 简单理解合一将 Blob  看做二进制容器,表示存放着一个大的二进制文件。Blob 对象有一个很重要的方法:slice(),这里需要注意的是 Blob 对象是不可变的,slice 方法返回的是一个新的 Blob,表示所需要切割的二进制文件。

slice() 方法接受三个参数,起始偏移量,结束偏移量,还有可选的 mime 类型。如果 mime 类型,没有设置,那么新的 Blob 对象的 mime 类型和父级一样。而 File 接口基于 Blob,File 对象也包含了slice方法,其结果包含有源 Blob 对象中指定范围的数据。

看完了切割的方法,我们就可以对二进制文件进行拆分了。拆分示例如下:

function sliceInPiece(file, piece = 1024 * 1024 * 5) {   let totalSize = file.size; // 文件总大小   let start = 0; // 每次上传的开始字节   let end = start + piece; // 每次上传的结尾字节   let chunks = []   while (start < totalSize) {     // 根据长度截取每次需要上传的数据     // File对象继承自Blob对象,因此包含slice方法     let blob = file.slice(start, end);      chunks.push(blob)      start = end;     end = start + piece;   }   return chunks }

获得文件切割后的数组后,就可以挨个调用接口上传至服务端。

let file =  document.querySelector("[name=file]").files[0];  const LENGTH = 1024 * 1024 * 0.1; let chunks = sliceInPiece(file, LENGTH); // 首先拆分切片  chunks.forEach(chunk=>{   let fd = new FormData();   fd.append("file", chunk);   post('/upload', fd) })

完成上传后再至服务端将切片文件拼接成完整文件,让 FileReader 对象从 Blob 中读取数据。

当然这里会遇到两个问题,其一是面对上传完成的一堆切片文件,服务端要如知道它们的正确顺序?其二是如果有多个大体积文件同时上传,服务端该如何判断哪个切片属于哪个文件呢?

前后顺序的问题,我们可以通过构造切片的 FormData 时增加参数的方式来处理。比如用参数 ChunkIndex 表示当前切片的顺序。

而第二个问题可以通过增加参数比如 sourceFile 等(值可以是当前大体积文件的完整路径或者更严谨用文件的 hash 值)来标记原始文件来源。这样服务端在获取到数据时,就可以知道哪些切片来自哪个文件以及切片之间的前后顺序。

如果暂时不方便自行构架,也可以考虑使用云服务,比如

标签:

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车