起因
原ATRI文本文件大小为1.6MB,而watch gt这破表加载300KB的文本文件时,不管是import的形式,还是storage的形式,亦或是file.read的形式,都会卡到重启。
于是,在此背景下研究了一个分块加载文本的代码格式
举个例子
我有如下一个json的文本列表,我就会在达到某一个条件时加载其中符合条件的那个。
PS:之前试过使用require分块加载,但是还是会报错,不知道是系统原因还是啥原因,人家小米全import加载都不会报错(
jsonDataList: [
'b999.json', 'b101.json', 'b102.json', 'b103.json', 'b111.json', 'b112.json', 'b113.json', 'b114.json',
'b121.json', 'b122.json', 'b123.json', 'b124.json', 'b200.json', 'b201.json', 'b202.json', 'b203.json',
'b204.json', 'b205.json', 'b206.json', 'b207.json', 'b301.json', 'b302.json', 'b303.json', 'b304.json',
'b401.json', 'b402.json', 'b403.json', 'b404.json', 'b405.json', 'b406.json', 'b407.json', 'b501.json',
'b601.json', 'b701.json'
],
如下代码为ATRI加载文本文件使用的方法
PS:原计划是将文本放在本地的,但是我不会读取读取本地文件,开发文档也没讲,寄!
async loadData(chapter) {
const num = this.jsonDataList[chapter];
// 将回调转换为Promise
return new Promise((resolve, reject) => {
file.readText({
uri: `internal://files/${num}`,
success: (data) => {
try {
const parsedData = JSON.parse(data.text);
resolve(parsedData); // 正确传递解析后的数据
} catch (e) {
reject("JSON解析失败");
}
},
fail: (_, code) => {
reject(`文件读取失败,错误码:${code}`);
}
});
});
},
最近在做一个可查找的词典,但是由于词库过大,加载200KB的索引会卡死,于是将索引分为26份,按照首字母(全部转化为小写)进行查找,不必将所有索引全部加载,实现按需查找的功能,并且也不会爆内存,完美!
下面给出我写的下载模块,因为watch gt不支持数据块传输,所以只能使用全部下载字节流的方式了:
import fetch from '@blueos.network.fetch'
import file from '@blueos.storage.file'
import prompt from '@blueos.window.prompt'
export default {
data: {
indexData: null,
jsonDataList: ['output_part_1.json'],
},
async fetchPromise(options) {
return new Promise((resolve, reject) => {
fetch.fetch({
...options,
success: resolve,
fail: reject,
});
});
},
async writeTextPromise(options) {
return new Promise((resolve, reject) => {
file.writeText({
...options,
success: resolve,
fail: (data, code) => reject({ data, code }),
});
});
},
async writeArrayBufferPromise(options) {
return new Promise((resolve, reject) => {
file.writeArrayBuffer({
...options,
success: resolve,
fail: (data, code) => reject({ data, code }),
});
});
},
async getFile(fileName) {
return new Promise((resolve, reject) => {
file.get({
uri: `internal://files/${fileName}`,
success: () => {
resolve(true);
},
fail: (_, code) => {
reject(`handling fail, code = ${code}`);
},
});
});
},
async downloadFile(data) {
prompt.showToast({ message: "开始下载" });
let totalNum = data.length;
let hasDownload = 0;
for (let i = 0; i < totalNum; i++) {
const fileName = data[i];
let isInFile = false;
try {
isInFile = await this.getFile(fileName);
} catch (error) {
console.log(error);
}
if (isInFile) hasDownload++;
if (!isInFile) {
try {
let fileUri = `internal://files/${fileName}`;
let baseUrl = 'https://chorblack.top'
const chunkResponse = await this.fetchPromise({
url: `${baseUrl}/app/dictionary/${fileName}`,
responseType: "arraybuffer",
});
const textData = chunkResponse.data;
await this.writeArrayBufferPromise({
uri: fileUri,
buffer: new Uint8Array(textData),
});
prompt.showToast({
message: `还剩${data.length - i - 1}`
})
hasDownload++;
} catch (error) {
console.error(`下载失败: ${fileName}`, error);
}
}
}
if (hasDownload==totalNum) prompt.showToast({ message: "下载完成" });
}
}
下面提供分块传输的方法:
<template>
<div class="wrapper">
<text class="title">{{ title }}</text>
<input class="btn" type="button" value="下载" onclick="onDetailBtnClick" />
<input
class="btn"
type="button"
value="跳转到列表"
onclick="onDetailList"
/>
</div>
</template>
<script>
import router from '@blueos.app.appmanager.router'
import fetch from '@blueos.network.fetch'
import prompt from '@blueos.window.prompt'
import file from '@blueos.storage.file'
export default {
data: {
title: '👏欢迎体验应用开发',
},
onDetailList() {
router.push({
uri: '/pages/DemoDetail',
})
},
onDetailBtnClick() {
this.downloadFile('b101.json');
},
downLoadTest() {
},
async fetchPromise(options) {
return new Promise((resolve, reject) => {
fetch.fetch({
...options,
success: resolve,
fail: reject,
});
});
},
async writeArrayBufferPromise(options) {
return new Promise((resolve, reject) => {
file.writeArrayBuffer({
...options,
success: resolve,
fail: (data, code) => reject({ data, code }),
});
});
},
async downloadFile(name, chunkSize = 4096) {
let downloaded = 0,
totalSize = 0,
startByte = 0;
let fileUri = `internal://files/${name}`;
let baseUrl = '支持分块传输的url'
// 0. 清空文件缓存
await this.writeArrayBufferPromise({
uri: fileUri,
buffer: new Uint8Array(0), // 写入空缓冲
append: false, // 非追加模式会覆盖文件
});
// 1. 获取文件总大小
try {
const totalSizeResponse = await this.fetchPromise({
url: `${baseUrl}/${name}`,
responseType: "arraybuffer",
header: { Range: "bytes=0-0" },
});
const contentRange = totalSizeResponse.headers["Content-Range"];
totalSize = contentRange ? parseInt(contentRange.split("/")[1]) : 0;
prompt.showToast({ message: `文件大小: ${totalSize}` });
} catch (error) {
prompt.showToast({ message: "获取文件大小失败" });
return;
}
// 2. 分块下载
while (downloaded < totalSize) {
const endByte = Math.min(startByte + chunkSize - 1, totalSize - 1);
try {
const chunkResponse = await this.fetchPromise({
url: `${baseUrl}/${name}`,
responseType: "arraybuffer",
header: { Range: `bytes=${startByte}-${endByte}` },
});
if (chunkResponse.code === 206) {
const data = chunkResponse.data;
//prompt.showToast({ message: `字节数:${data.length}` });
await this.writeArrayBufferPromise({
uri: fileUri,
buffer: new Uint8Array(data),
append: true, // 追加模式
});
downloaded += data.byteLength;
startByte = endByte + 1;
prompt.showToast({ message:`Progress: ${((downloaded / totalSize) * 100).toFixed(1)}%` });
console.log(`Progress: ${((downloaded / totalSize) * 100).toFixed(1)}%`);
} else {
prompt.showToast({ message: `错误代码:${chunkResponse.code}` });
return;
}
} catch (error) {
prompt.showToast({ message: error });
return;
}
}
prompt.showToast({ message: "下载完成" });
}
}
</script>
<style lang="scss">
@import './../../assets/styles/style.scss';
.wrapper {
@include flex-box-mixins(column, center, center);
.title {
font-size: 7 * $size-factor;
text-align: center;
color: $black;
}
.btn {
width: 55 * $size-factor;
height: 12 * $size-factor;
border-radius: 7 * $size-factor;
background-color: $brand;
color: $white;
font-size: 5 * $size-factor;
margin-top: 7 * $size-factor;
}
}
</style>