前端导入txt文件并转换为json
发表于:2023-10-08 |

前言

我最近遇到个需求,需要请求接口获取txt文件获取内容,并且将数据存在浏览器本地,而且可以手动指定更新存储内容,因此我就想着把数据存localStorage里面,但是localStorage只能存字符串,所以我就想着将txt文件转换为json文件,然后存储到localStorage里面,这样就可以实现我想要的功能了。

txt文件放置本地

从接口读取不方便大家测试,因此我将txt文件放在了本地,本地和接口读取的方法是一样的。
效果图

文件列表

我将我的文件依次按照name和path对应,然后后面我会把name当作key,txt获取到的内容当作value,存储到localStorage里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const filePathList=[
{
path:"1",
name:"监管方式"
},
{
path:"2",
name:"国家"
},
{
path:"3",
name:"指运港"
},
{
path:"4",
name:"境内货源地"
},
{
path:"5",
name:"运输方式"
},
{
path:"6",
name:"商品_征免"
},
{name:'征免性质', path:"7"},
{name:'货币', path:"8"},
{name:'成交方式', path:"9"},
{name:'包装种类', path:"10"},
{name:'单位', path:"11"},
{name:'运费保费杂费种类', path:"12"},
{name:'随附单证', path:"13"},
{name:'随附单据类型', path:"14"},
{name:'集装箱', path:"15"},
{name:'产地', path:"16"},
{name:'报关单类型', path:"17"},
{name:'危险品_危包类别', path:"18"},
{name:'离境口岸', path:"19"},
{name:'出境关别', path:"20"},
{name:'老国家代码', path:"21"},
{name:'进口商检_海关机关代码', path:"22"},
{name:'进口商检_企业资质类别', path:"23"},
{name:'进口商检_关联理由', path:"24"},
{name:'进口商检_表体_用途', path:"25"},
{name:'进口商检_表体_许可证类别', path:"26"},
{name:'核注清单_清单类型', path:"27"},
{name:'核注清单_料件成品标志', path:"28"},
{name:'老币制代码', path:"29"},
{name:'核注清单_报关标志', path:"30"},
{name:'核注清单_是否系统生成报关单', path:"31"},
{name:'核注清单_报关类型', path:"32"},
{name:'核注清单_报关单类型', path:"33"},
{name:'优惠贸易协定', path:"34"},
{name:'指运港_不带国内', path:"103"},
{name:'通用_是否', path:"50"},
{name:'出口商检_表体_许可证类别', path:"51"},
]

读取文件方法

这里我使用了xhr的读取文件方法,xhr.overrideMimeType(text/plain; charset=${charsetType});指定了读取文件的编码格式,这里我使用的是GBK,因为我的txt文件是GBK编码的,如果不指定的话,会出现乱码的情况。你也可以根据实际情况指定其他编码格式,如utf-8等,使用 if (line.trim() === '') { continue; }我过滤了空行,因为我的txt文件里面有空行,如果不过滤的话,会出现空行的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type TFileData=Promise<[string,{label:string,value:string}[]]>
// 导入文件
function importTxtFile(filePath:string,name:string,charsetType:string="GBK"):TFileData {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', filePath, false);
xhr.overrideMimeType(`text/plain; charset=${charsetType}`);
xhr.send();

if (xhr.readyState === 4 && xhr.status === 200) {
const content = xhr.responseText;
const lines = content.split('\n');
const jsonData = [];

for (let line of lines) {
if (line.trim() === '') {
continue;
}

const [key, value] = line.split(':').map(item => item.trim());
jsonData.push({label:key, value});
}

resolve([name,jsonData]);
} else {
reject('读取文件失败');
}
})

}

循环调用读取文件方法

这里我使用了promise.all方法,将所有的promise放到一个数组里面,然后使用promise.all方法,等待所有的promise执行完毕,然后将所有的数据存储到localStorage里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 存储文件数据
const importTxtDataToJSON=()=>{
const initTime=new Date().getTime()
const promise:TFileData[]=[]
filePathList.forEach((item)=>{
promise.push(importTxtFile(`/txt/${item.path}.txt`,item.name))
})
Promise.all(promise).then((value)=>{
const str=JSON.stringify(Object.fromEntries(value))
const curTime=new Date().getTime()
console.log("数据",Object.fromEntries(value))
console.log("加载时间",curTime-initTime)
console.log("数据大小",getJsonLength(str))
localStorage.setItem("testData",str)
})
}

数据大小方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 判断json串的大小
function getJsonLength(jsonString:string){
// 将 JSON 字符串转换为 JavaScript 对象
const jsonObject = JSON.parse(jsonString);

// 将 JavaScript 对象重新转换为 JSON 字符串
const jsonStringified = JSON.stringify(jsonObject);

// 将 JSON 字符串编码为 Uint8Array 类型的数据
const encoder = new TextEncoder();
const encodedData = encoder.encode(jsonStringified);

// 获取 JSON 字符串的内存大小
const memorySize = encodedData.byteLength;

return memorySize // 输出 JSON 字符串的内存大小(字节数)
}

完整代码添加localStorage已有不查逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<template>
<div @click="importTxtDataToJSON">测试txt导入数据</div>
</template>

<script setup lang="ts">
import {onMounted} from "vue";

type TFileData=Promise<[string,{label:string,value:string}[]]>

const filePathList=[
{
path:"1",
name:"监管方式"
},
{
path:"2",
name:"国家"
},
{
path:"3",
name:"指运港"
},
{
path:"4",
name:"境内货源地"
},
{
path:"5",
name:"运输方式"
},
{
path:"6",
name:"商品_征免"
},
{name:'征免性质', path:"7"},
{name:'货币', path:"8"},
{name:'成交方式', path:"9"},
{name:'包装种类', path:"10"},
{name:'单位', path:"11"},
{name:'运费保费杂费种类', path:"12"},
{name:'随附单证', path:"13"},
{name:'随附单据类型', path:"14"},
{name:'集装箱', path:"15"},
{name:'产地', path:"16"},
{name:'报关单类型', path:"17"},
{name:'危险品_危包类别', path:"18"},
{name:'离境口岸', path:"19"},
{name:'出境关别', path:"20"},
{name:'老国家代码', path:"21"},
{name:'进口商检_海关机关代码', path:"22"},
{name:'进口商检_企业资质类别', path:"23"},
{name:'进口商检_关联理由', path:"24"},
{name:'进口商检_表体_用途', path:"25"},
{name:'进口商检_表体_许可证类别', path:"26"},
{name:'核注清单_清单类型', path:"27"},
{name:'核注清单_料件成品标志', path:"28"},
{name:'老币制代码', path:"29"},
{name:'核注清单_报关标志', path:"30"},
{name:'核注清单_是否系统生成报关单', path:"31"},
{name:'核注清单_报关类型', path:"32"},
{name:'核注清单_报关单类型', path:"33"},
{name:'优惠贸易协定', path:"34"},
{name:'指运港_不带国内', path:"103"},
{name:'通用_是否', path:"50"},
{name:'出口商检_表体_许可证类别', path:"51"},
]

onMounted(() => {
!localStorage.getItem('testData')&&importTxtDataToJSON()
})

// 存储文件数据
const importTxtDataToJSON=()=>{
const initTime=new Date().getTime()
const promise:TFileData[]=[]
filePathList.forEach((item)=>{
promise.push(importTxtFile(`/txt/${item.path}.txt`,item.name))
})
Promise.all(promise).then((value)=>{
const str=JSON.stringify(Object.fromEntries(value))
const curTime=new Date().getTime()
console.log("数据",Object.fromEntries(value))
console.log("加载时间",curTime-initTime)
console.log("数据大小",getJsonLength(str))
localStorage.setItem("testData",str)
})
}

// 导入文件
function importTxtFile(filePath:string,name:string,charsetType:string="GBK"):TFileData {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', filePath, false);
xhr.overrideMimeType(`text/plain; charset=${charsetType}`);
xhr.send();

if (xhr.readyState === 4 && xhr.status === 200) {
const content = xhr.responseText;
const lines = content.split('\n');
const jsonData = [];

for (let line of lines) {
if (line.trim() === '') {
continue;
}

const [key, value] = line.split(':').map(item => item.trim());
jsonData.push({label:key, value});
}

resolve([name,jsonData]);
} else {
reject();
}
})

}

// 判断json串的大小
function getJsonLength(jsonString:string){
// 将 JSON 字符串转换为 JavaScript 对象
const jsonObject = JSON.parse(jsonString);

// 将 JavaScript 对象重新转换为 JSON 字符串
const jsonStringified = JSON.stringify(jsonObject);

// 将 JSON 字符串编码为 Uint8Array 类型的数据
const encoder = new TextEncoder();
const encodedData = encoder.encode(jsonStringified);

// 获取 JSON 字符串的内存大小
const memorySize = encodedData.byteLength;

return memorySize // 输出 JSON 字符串的内存大小(字节数)
}
</script>

效果图
效果图

拓展indexDB存储

此时我们已经将txt文件转换为json文件,并且存储到了localStorage里面,但是如果我们的文件非常大呢,我现在这个只有0.8多一些的MB,我们都知道,如果数据大于5MB,存储在localStorage是不合适的,这时不知道大家平时开发的时候,浏览器应用程序那里,是不是除了localStorage,sessionStorage,cookie还有别的呢。
效果图
我这里就使用indexDB进行数据存储,indexDB是浏览器提供的本地数据库,可以存储大量的数据,但是indexDB的api比较复杂,我这里就不做详细介绍了,大家可以自行百度,我这里只是简单的介绍一下,如何使用indexDB存储数据。

定义一个类

如果是批量存储数据的话,用put的性能会比add好一些,因为put是覆盖,add是新增,如果是单条数据的话,用add就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class IndexedDBWrapper {
constructor(dbName, version, storeName) {
this.dbName = dbName;
this.version = version;
this.storeName = storeName;
this.db = null;
}

openDB() {
return new Promise((resolve, reject) => {
const request = window.indexedDB.open(this.dbName, this.version);

request.onerror = () => {
reject('无法打开数据库');
};

request.onsuccess = () => {
this.db = request.result;
resolve();
};

request.onupgradeneeded = (event) => {
this.db = event.target.result;
if (!this.db.objectStoreNames.contains(this.storeName)) {
this.db.createObjectStore(this.storeName, { keyPath: 'id' });
}
};
});
}

closeDB() {
if (this.db) {
this.db.close();
this.db = null;
}
}

addRecord(record) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite');
const store = transaction.objectStore(this.storeName);
const request = store.add(record);

request.onsuccess = () => {
resolve(request.result);
};

request.onerror = () => {
reject('添加记录失败');
};
});
}

getRecord(id) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.get(id);

request.onsuccess = () => {
resolve(request.result);
};

request.onerror = () => {
reject('获取记录失败');
};
});
}

getAllRecords() {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const request = store.getAll();

request.onsuccess = () => {
resolve(request.result);
};

request.onerror = () => {
reject('获取所有记录失败');
};
});
}
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const db = new IndexedDBWrapper('myDB', 1, 'myStore');

db.openDB().then(() => {
const record = { id: 1, name: 'John', age: 25 };

db.addRecord(record).then(() => {
console.log('记录添加成功');
}).catch(error => {
console.error(error);
});

db.getRecord(1).then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});

db.getAllRecords().then(data => {
console.log(data);
}).catch(error => {
console.error(error);
});
});

结语

本篇文章就讲到这里,更多内容敬请期待

上一篇:
spa刷新组件而非页面
下一篇:
【JAVA学习】01-DOS命令学习