最近做项目时遇到了一些需求,要实现pdf,excel,csv等文件格式的预览。在网上搜索了下,关于pdf,excel预览的比较多,关于csv的就比较少,所以我想将我开发csv预览的过程整理一下。
gitHub源码地址:github.com/seapack-hub…
第一步,先了解csv文件的格式。
csv全称“Comma-Separated Values”,是一种逗号分隔值格式的文件,是一种用来存储数据的纯文本格式文件。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串。
了解之后发现csv是一种纯文本格式文件,因此在进行预览的时,可以将文本转成对应的数组格式,然后将数组填充到创建的表格中。
第二步,解析csv文件。
将获取到的文件转化为二进制文件。
//获取文件
async handleChange(e) {
this.loading = true;
try {
//提取出前端浏览器解析出的file文件
const [file] = e.target.files;
//解析file文件,生成二进制数据
const arrayBuffer = await readBuffer(file);
this.loading = false;
this.last = await this.displayResult(arrayBuffer, file);
} catch (e) {
console.error(e);
} finally {
this.loading = false;
}
},
//将解析好的文件放入界面中
displayResult(buffer, file) {
// 取得文件名
const { name } = file;
// 取得扩展名
const extend = getExtend(name);
// 输出目的地
const { output } = this.$refs;
// 生成新的dom
const node = document.createElement("div");
// 添加孩子,防止vue实例替换dom元素
if (this.last) {
output.removeChild(this.last.$el);
this.last.$destroy();
}
const child = output.appendChild(node);
// 调用渲染方法进行渲染
return new Promise((resolve, reject) =>
render(buffer, extend, child).then(resolve).catch(reject)
);
},
通过FileReader 按照文件的编码将二进制文件转化为Text文本
export async function readText(buffer) {
return new Promise((resolve,reject)=>{
let readerBs64 = new FileReader();
readerBs64.readAsDataURL(new Blob([buffer]));
readerBs64.onload = () => {
let str = atob(readerBs64.result.split(';base64,')[1])
// 要用二进制格式,获取文件编码
let encoding = jschardet.detect(str)
encoding = encoding.encoding;
console.log('获取文件编码',encoding)
let reader = new FileReader();
if(encoding == 'GB2312'){
reader.readAsText(new Blob([buffer]),'gbk')
}else{
reader.readAsText(new Blob([buffer]),encoding)
}
reader.onload = (loadEvent)=>{
const {result} = loadEvent.target;
resolve(result);
}
};
readerBs64.onerror = e => reject(e);
})
}
将csv文本转化为数组
export function csvToArray(csv){
let table = [];
//将字符串以换行符隔开。
let allRows = csv.split(/\r\n/);
for(let i=0; i<allRows.length; i++){
if(allRows[i] != ""){
table.push(allRows[i].split(","));
}
}
return table;
}
将数组展示在vue模板中
新建vue实例
import Vue from 'vue';
import Table from './Table';
import {readText} from "@/components/util.js";
import ElementUI from 'element-ui'; // 2.1引入结构
import 'element-ui/lib/theme-chalk/index.css'; // 2.2引入样式
Vue.use(ElementUI); // 3.安装
export default async function renderCsv(buffer, target) {
const table = await readText(buffer);
return new Vue({
render: h => h(Table, { props: { value: table } }),
}).$mount(target)
}
将数组转化为模板可以识别的数据形式。
<template>
<div>
<!-- {{value}}-->
<el-table class="container-table" ref="table" :data="rows" border height="100%" :header-cell-style="{'background':'#778da8','color':'#fff'}">
<el-table-column v-for="(column, index) in columns" :key="index" :prop="column" :label="column">
<template slot-scope="scope">
<div>
{{ scope.row[column] }}
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import {readText,csvToArray} from "./util";
export default {
name: "Table",
props: {
value: {
type: String,
default:''
}
},
data(){
return {
rows: [],
columns: [],
}
},
watch:{
value:{
immediate:true,
handler(value){
this.setTable(value);
}
}
},
methods:{
setTable(value){
let data = csvToArray(value);
for(let i=0;i<data.length;i++){
if(i==0){
this.columns = data[i];
}else{
let obj = {};
data[i].forEach((e,index)=>{
obj[data[0][index]]= e;
})
this.rows.push(obj);
}
}
}
}
}
</script>
<style scoped>
</style>
实现效果:
拓展:如何解决csv展示中出现的中文乱码问题
为什么会产生中文乱码呢?
如果表格内容有中文的话,就是个大问题了。因为一般网页的编码是UTF8,导出的表格也会是UTF8编码格式,如果不修改直接上传则为UTF8。但是如果修改,Windows平台下的常用表格软件包括Office和WPS全都将其转换成GBK编码。如果程序没有自动识别编码处理,将有很大概率导致乱码。
另一方面,如果网页使用GBK编码格式下载,也不能确保用户上传的文件就一定是GBK,因为MAC系统用的是UTF8,可能本来GBK的在修改后就成了UTF8了。
解决方案:先将服务器上的文件获取到,然后用FileReader根据编码格式不同按不同的编码格式读取到文件数据。
可以通过jschardet插件来获取文件的编码格式。
export async function readText(buffer) {
return new Promise((resolve,reject)=>{
let readerBs64 = new FileReader();
readerBs64.readAsDataURL(new Blob([buffer]));
readerBs64.onload = () => {
let str = atob(readerBs64.result.split(';base64,')[1])
// 要用二进制格式,获取文件编码
let encoding = jschardet.detect(str)
encoding = encoding.encoding;
console.log('获取文件编码',encoding)
let reader = new FileReader();
if(encoding == 'GB2312'){
reader.readAsText(new Blob([buffer]),'gbk')
}else{
reader.readAsText(new Blob([buffer]),encoding)
}
reader.onload = (loadEvent)=>{
const {result} = loadEvent.target;
resolve(result);
}
};
readerBs64.onerror = e => reject(e);
})
}