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

当这个文件数据量也不是很多的时候,有很多前端工具可供选择。例如 SheetJS,就提供了从 Excel、CSV 中解析出用信息的很多方法,十分方便。
当数据量只是几千条的程度的,选择的余地很多,但是一旦数据量级增加,处理就变得复杂。如果 XLSX/CSV 数据量达到了 100w+ 条,Office、WPS 想打开看一下,都会需要很长的时间。
那又该如何从这样大体积的 Excel/CSV/TXT 中解析出数据呢?
背景
下面我们通过一个假设的需求,来讲述理解整个过程。假设我们需求是从本地 Excel、CSV、TXT(或者其他格式的)文件中解析出数据,并经过清洗后存入本地数据库文件中。但是这些文件体积可能是 5M、50M、500M 甚至更大。那么在浏览器环境下如何上传?Node 环境下应该如何解析?
首先,我们需要了解的是浏览器 Web 页面如何上传大体积文件?
Web 页面如何上传大体积文件?
Web 页面一般也是可以上传大文件的,但是会面临一个问题。如果要上传的数据比较大,那么整个上传过程会比较漫长,再加上上传过程的不确定因素,一旦失败,那整个上传就要从头再来,耗时很长。
面对这个问题,我们可以通过将大文件分成多份小文件,每一次只上传一份的方法来解决。这样即使某个请求失败了,也无需从头开始,只要重新上传失败的那一份就好了。
如果想要使用这个方法,我们需要满足以下几项需求:
- 大体积文件支持切片上传
- 可以断点续传
- 可以得知上传进度
首先看一下如何进行大文件切割。Web 页面基本都是通过 来获取本地文件的。 而通过 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 值)来标记原始文件来源。这样服务端在获取到数据时,就可以知道哪些切片来自哪个文件以及切片之间的前后顺序。
如果暂时不方便自行构架,也可以考虑使用云服务,比如又拍云存储就支持大文件上传和断点续传的。比如:
断点续传
在上传大文件或移动端上传文件时,因为网络质量、传输时间过长等原因造成上传失败,可以使用断点续传。特别地,断点续传上传的图片不支持预处理。特别地,断点续传上传的文件不能使用其他上传方式覆盖,如果需要覆盖,须先删除文件。
\
名称概念
- 文件分块:直接切分二进制文件成小块。分块大小固定为 1M。最后一个分块除外。
- 上传阶段:使用 x

本文介绍了在前端和Node环境中处理大体积XLSX/CSV/TXT文件的方法。对于前端,通过File对象的slice()方法进行文件切割,使用分片上传和断点续传技术,如又拍云存储支持的方案。在Node环境中,利用fs模块和stream流进行大文件读取,通过createReadStream和createWriteStream方法实现流式处理,从而减少内存压力。对于CSV和XLSX文件,可以转换为CSV格式,利用fast-csv库进行解析,或者直接使用sax解析XLSX文件。
最低0.47元/天 解锁文章
2057

被折叠的 条评论
为什么被折叠?



