guoyu/fronted_uniapp/utils/modelDownloader.js

207 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 语音模型下载管理器
* 用于在APP运行时下载和管理语音识别模型
*/
class ModelDownloader {
constructor() {
this.modelUrl = 'https://app.liuyingyong.cn/static/vosk-model-small-cn-0.22.zip';
this.modelFileName = 'vosk-model-small-cn-0.22.zip';
this.modelSize = 41.87 * 1024 * 1024; // 41.87 MB
}
/**
* 获取模型本地路径
*/
getModelPath() {
// #ifdef APP-PLUS
return plus.io.convertLocalFileSystemURL('_doc/' + this.modelFileName);
// #endif
// #ifndef APP-PLUS
return '';
// #endif
}
/**
* 检查模型是否已下载
*/
checkModelExists() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
const localPath = this.getModelPath();
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
// 文件存在,检查大小
entry.file((file) => {
if (file.size > 0) {
console.log('模型文件已存在,大小:', file.size);
resolve({
exists: true,
path: localPath,
size: file.size
});
} else {
console.log('模型文件存在但大小为0需要重新下载');
resolve({
exists: false
});
}
}, () => {
resolve({
exists: false
});
});
}, () => {
// 文件不存在
console.log('模型文件不存在');
resolve({
exists: false
});
});
// #endif
// #ifndef APP-PLUS
resolve({
exists: false
});
// #endif
});
}
/**
* 下载模型
* @param {Function} onProgress 进度回调 (progress, speed)
* @param {Function} onSuccess 成功回调 (filePath)
* @param {Function} onError 失败回调 (error)
*/
downloadModel(onProgress, onSuccess, onError) {
console.log('开始下载模型:', this.modelUrl);
const localPath = this.getModelPath();
let startTime = Date.now();
let lastLoaded = 0;
const downloadTask = uni.downloadFile({
url: this.modelUrl,
filePath: localPath,
timeout: 300000, // 5分钟超时
success: (res) => {
if (res.statusCode === 200) {
console.log('模型下载成功:', res.tempFilePath);
// 验证文件大小
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {
if (file.size > 10 * 1024 * 1024) { // 至少10MB
console.log('模型文件验证通过,大小:', file.size);
onSuccess && onSuccess(localPath);
} else {
console.error('模型文件大小异常:', file.size);
onError && onError(new Error('文件大小异常'));
}
}, (err) => {
onError && onError(err);
});
}, (err) => {
onError && onError(err);
});
// #endif
// #ifndef APP-PLUS
onSuccess && onSuccess(localPath);
// #endif
} else {
console.error('下载失败,状态码:', res.statusCode);
onError && onError(new Error('下载失败:' + res.statusCode));
}
},
fail: (err) => {
console.error('下载失败:', err);
onError && onError(err);
}
});
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
const progress = res.progress;
const totalBytesWritten = res.totalBytesWritten;
const totalBytesExpectedToWrite = res.totalBytesExpectedToWrite;
// 计算下载速度
const now = Date.now();
const elapsed = (now - startTime) / 1000; // 秒
const downloaded = totalBytesWritten - lastLoaded;
const speed = downloaded / elapsed; // 字节/秒
lastLoaded = totalBytesWritten;
startTime = now;
console.log('下载进度:', progress + '%', '速度:', (speed / 1024).toFixed(2) + ' KB/s');
onProgress && onProgress({
progress: progress,
loaded: totalBytesWritten,
total: totalBytesExpectedToWrite,
speed: speed
});
});
return downloadTask;
}
/**
* 删除模型文件
*/
deleteModel() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
const localPath = this.getModelPath();
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.remove(() => {
console.log('模型文件已删除');
resolve();
}, (err) => {
console.error('删除失败:', err);
reject(err);
});
}, () => {
// 文件不存在
resolve();
});
// #endif
// #ifndef APP-PLUS
resolve();
// #endif
});
}
/**
* 格式化文件大小
*/
formatSize(bytes) {
if (bytes < 1024) {
return bytes + ' B';
} else if (bytes < 1024 * 1024) {
return (bytes / 1024).toFixed(2) + ' KB';
} else if (bytes < 1024 * 1024 * 1024) {
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
} else {
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
}
}
/**
* 格式化下载速度
*/
formatSpeed(bytesPerSecond) {
return this.formatSize(bytesPerSecond) + '/s';
}
}
// 导出单例
export default new ModelDownloader();