1.2.0部分代码
This commit is contained in:
+13
-1
@@ -113,4 +113,16 @@ export { compressedImage,saveImageToGallery,CompressedImageInfo } from './src/ma
|
||||
|
||||
export { ScreeningView } from './src/main/ets/Views/ScreeningView'
|
||||
|
||||
export { TagListModel, TagList} from './src/main/ets/models/TagListModel'
|
||||
export { TagListModel, TagList} from './src/main/ets/models/TagListModel'
|
||||
|
||||
export { AppUtil } from './src/main/ets/utils/AppUtil'
|
||||
|
||||
export { OnWXResp,WXApi,WXEventHandler } from './src/main/ets/models/WXApiWrap'
|
||||
|
||||
// export { DBManager,FileDownloadManager } from './src/main/ets/utils/FileManager'
|
||||
|
||||
export { FileManager } from './src/main/ets/utils/FileManager'
|
||||
|
||||
export { FileInfo,DownloadCallback,PreviewConfig } from './src/main/ets/models/FileModel'
|
||||
|
||||
export { FileDownloadManager } from './src/main/ets/utils/FileDownloadManager'
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ChangeUtil } from "../../../../Index";
|
||||
import { promptAction } from "@kit.ArkUI";
|
||||
|
||||
@CustomDialog
|
||||
export struct DefaultHintProWindows {
|
||||
@@ -12,16 +14,23 @@ export struct DefaultHintProWindows {
|
||||
@Prop cancleColor:ResourceStr = '#FFFFFF';
|
||||
@Prop confirmColor:ResourceStr = '#FFFFFF';
|
||||
controller: CustomDialogController;
|
||||
@State inputContent:string = ''
|
||||
|
||||
// 添加回调函数属性
|
||||
private selectedButton: (index: number) => void = () => {};
|
||||
|
||||
private selectedButtonAndContent: (index: number,content:string) => void = () => {};
|
||||
|
||||
// 修改构造函数
|
||||
constructor(controller: CustomDialogController, selectedButton: (index:number) => void) {
|
||||
super();
|
||||
this.controller = controller;
|
||||
this.selectedButton = selectedButton;
|
||||
}
|
||||
constructor(controller:CustomDialogController,selectedButtonAndContent: (index: number,content:string) => void) {
|
||||
super();
|
||||
this.controller = controller
|
||||
this.selectedButtonAndContent = selectedButtonAndContent
|
||||
}
|
||||
|
||||
build() {
|
||||
Row(){
|
||||
@@ -32,10 +41,21 @@ export struct DefaultHintProWindows {
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ top: 20 })
|
||||
|
||||
Text(this.message)
|
||||
.fontSize(this.messageFont)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ top: 10 })
|
||||
if (!ChangeUtil.stringIsUndefinedAndNull(this.message)) {
|
||||
Text(this.message)
|
||||
.fontSize(this.messageFont)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ top: 10 })
|
||||
.padding({left:10,right:10})
|
||||
} else {
|
||||
TextInput({placeholder:'请输入您的登录密码'})
|
||||
.margin(10)
|
||||
.backgroundColor('#f4f4f4')
|
||||
.borderRadius(5)
|
||||
.onChange((value:string)=>{
|
||||
this.inputContent = value
|
||||
})
|
||||
}
|
||||
|
||||
Row({ space: 20 }) {
|
||||
Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
|
||||
@@ -45,7 +65,11 @@ export struct DefaultHintProWindows {
|
||||
}
|
||||
.width('45%').height(30).backgroundColor(this.cancleColor)
|
||||
.onClick(() => {
|
||||
this.selectedButton(0)
|
||||
if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
|
||||
this.selectedButtonAndContent(0,'')
|
||||
} else {
|
||||
this.selectedButton(0)
|
||||
}
|
||||
})
|
||||
.visibility(this.cancleTitle?Visibility.Visible:Visibility.None)
|
||||
|
||||
@@ -55,7 +79,15 @@ export struct DefaultHintProWindows {
|
||||
.fontColor(this.confirmTitleColor)
|
||||
}.width('45%').height(30).backgroundColor(this.confirmColor)
|
||||
.onClick(() => {
|
||||
this.selectedButton(1)
|
||||
if (ChangeUtil.stringIsUndefinedAndNull(this.message)) {
|
||||
if (ChangeUtil.stringIsUndefinedAndNull(this.inputContent)) {
|
||||
promptAction.showToast({ message: '请输入您的登录密码', duration: 1000 })
|
||||
return
|
||||
}
|
||||
this.selectedButtonAndContent(1,this.inputContent)
|
||||
} else {
|
||||
this.selectedButton(1)
|
||||
}
|
||||
})
|
||||
}.margin({ top: 20, bottom: 20 })
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export class BasicConstant {
|
||||
static readonly wxUrl = "https://dev-wx.igandan.com/";
|
||||
static readonly polvId = "11";//保利威视学员id
|
||||
static readonly urlApp="https://dev-app.igandan.com/app/"
|
||||
static readonly expertPay = "https://dev-app.igandan.com/app/expertPay/"
|
||||
//正式环境
|
||||
// static readonly urlExpertAPI = "https://app.igandan.com/app/expertAPI/";
|
||||
// static readonly urlExpertApp = "http://app.igandan.com/app/expertApp/"
|
||||
@@ -23,7 +24,9 @@ export class BasicConstant {
|
||||
// static readonly wxUrl = "https://wx.igandan.com/";// 微信服务器地址
|
||||
// static readonly polvId = "21";//保利威视学员id
|
||||
// static readonly urlApp="http://app.igandan.com/app/"
|
||||
// static readonly expertPay = "https://app.igandan.com/app/expertPay/"
|
||||
|
||||
static readonly getSystemTime = BasicConstant.urlApp + 'manager/' + 'getSystemTime'
|
||||
static readonly getSystemTimeStamp = BasicConstant.urlApp+'manager/getSystemTimeStamp'
|
||||
static readonly addBonusPoints = BasicConstant.urlExpertApp+'addBonusPoints'
|
||||
static readonly indexV2 = BasicConstant.urlExpertAPI+'indexV2';//首页轮播
|
||||
@@ -72,12 +75,33 @@ export class BasicConstant {
|
||||
static readonly deleteComment = BasicConstant.urlExpertApp+'deleteComment';//删除评论
|
||||
static readonly meetingHistoryList = BasicConstant.urlExpertAPI + "meetingHistoryList";
|
||||
static readonly videoRoll = BasicConstant.urlExpertAPI + "videoRoll";
|
||||
static readonly newsRollNew = BasicConstant.urlExpert + 'newsRollNew'
|
||||
static readonly newsTagList = BasicConstant.urlExpert + 'newsTagList'
|
||||
static readonly newsListNew = BasicConstant.urlExpert + 'newsListNew'
|
||||
static readonly defaultNewsListNew = BasicConstant.urlExpert + 'defaultNewsListNew'
|
||||
static readonly expertVideoTypeList = BasicConstant.urlExpertAPI + "expertVideoTypeList";
|
||||
static readonly patientVideoNew = BasicConstant.urlExpertApp + 'patientVideoNew';
|
||||
static readonly videoByKeyWordsNew = BasicConstant.urlExpertApp + "videoByKeyWordsNew";
|
||||
static readonly patientVideoByKeyWordsNew = BasicConstant.urlExpertApp + 'patientVideoByKeyWordsNew'
|
||||
static readonly feedBack = BasicConstant.urlExpert+'feedBack'
|
||||
static readonly ganDanFileByKeyWords = BasicConstant.urlExpertAPI+'ganDanFileByKeyWords'//肝胆课件
|
||||
static readonly getKePuCollection = BasicConstant.urlExpert+'getKePuCollection'
|
||||
static readonly discollection = BasicConstant.urlExpert + 'discollection'
|
||||
static readonly collection = BasicConstant.urlExpert + 'collection'
|
||||
static readonly disagree = BasicConstant.urlExpert + 'disagree'
|
||||
static readonly agree = BasicConstant.urlExpert + 'agree'
|
||||
static readonly ganDanFileDetials = BasicConstant.urlExpertAPI + 'ganDanFileDetials'
|
||||
static readonly newsDetial = BasicConstant.urlExpert + 'newsDetial'
|
||||
static readonly createGanDanFileOrder = BasicConstant.expertPay + 'createGanDanFileOrder'
|
||||
static readonly getBalance = BasicConstant.expertPay + 'getBalance'
|
||||
static readonly payGanDanFileOrder = BasicConstant.expertPay + 'payGanDanFileOrder'
|
||||
static readonly getOrderStatus = BasicConstant.expertPay + 'getOrderStatus'
|
||||
static readonly useWelfareNum = BasicConstant.urlExpertAPI + 'useWelfareNum'
|
||||
static readonly allXinyiList = BasicConstant.expertPay + 'allXinyiList'
|
||||
static readonly xinyiPrice = BasicConstant.expertPay + 'xinyiPrice'
|
||||
static readonly createXinYiOrder = BasicConstant.expertPay + 'createXinYiOrder'
|
||||
static readonly payXinYiOrder = BasicConstant.expertPay + 'payXinYiOrder'
|
||||
static readonly getFlowerList = BasicConstant.expertPay + 'getFlowerList'
|
||||
static readonly tagList = BasicConstant.urlExpertApp + "tagList";
|
||||
static readonly meetingListBySearch = BasicConstant.urlExpertAPI + "meetingListBySearch";
|
||||
static readonly videoBySearchNew = BasicConstant.urlExpertApp+'videoBySearchNew';//搜索肝胆视频列表
|
||||
@@ -135,6 +159,8 @@ export class BasicConstant {
|
||||
static readonly notification_back_refreshData = 250529;//返回上页通知刷新数据
|
||||
//首页tabContent切换事件通知
|
||||
static readonly notification_home_tab_change = 25060413;
|
||||
//课件支付成功返回上一页下载科技
|
||||
static readonly notification_kejian_pay_success = 202509041128;
|
||||
|
||||
|
||||
static readonly YX_accid='YX_accid'//云信
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const APP_ID = "wxbf3658f5e674667c"
|
||||
export const APP_SECRET = "c4505a04a9910c65efea8e11ffc93f92"
|
||||
@@ -0,0 +1,51 @@
|
||||
// 文件信息接口
|
||||
export interface FileInfo {
|
||||
fileId: string;
|
||||
fileName: string;
|
||||
fileType: string;
|
||||
fileSize: number;
|
||||
fileUrl: string;
|
||||
filePath?: string;
|
||||
downloadStatus: 'pending' | 'downloading' | 'downloaded' | 'failed' | 'cancelled';
|
||||
downloadProgress: number;
|
||||
createTime: number;
|
||||
updateTime: number;
|
||||
}
|
||||
|
||||
// 下载进度回调
|
||||
export interface DownloadCallback {
|
||||
onProgress?: (progress: number) => void;
|
||||
onSuccess?: (filePath: string) => void;
|
||||
onFailed?: (error: string) => void;
|
||||
onCancelled?: () => void;
|
||||
}
|
||||
|
||||
// 文件预览配置
|
||||
export interface PreviewConfig {
|
||||
enableCache?: boolean;
|
||||
maxCacheSize?: number;
|
||||
supportedTypes?: string[];
|
||||
}
|
||||
|
||||
// 定义下载状态枚举
|
||||
export enum DownloadStatus {
|
||||
PENDING = 'pending', // 等待中
|
||||
DOWNLOADING = 'downloading', // 下载中
|
||||
PAUSED = 'paused', // 已暂停
|
||||
COMPLETED = 'completed', // 已完成
|
||||
FAILED = 'failed', // 失败
|
||||
}
|
||||
|
||||
// 定义文件项接口
|
||||
export interface DownloadFileItem {
|
||||
id: string; // 文件唯一标识
|
||||
name: string; // 文件名
|
||||
url: string; // 下载地址
|
||||
localPath: string; // 本地存储路径
|
||||
size: number; // 文件大小(字节)
|
||||
downloadedSize: number; // 已下载大小(字节)
|
||||
status: DownloadStatus; // 下载状态
|
||||
createTime: number; // 创建时间
|
||||
finishTime?: number; // 完成时间
|
||||
error?: string; // 错误信息(失败时)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as wxopensdk from '@tencent/wechat_open_sdk';
|
||||
import { APP_ID } from '../constants/Constants';
|
||||
|
||||
export type OnWXReq = (req: wxopensdk.BaseReq) => void
|
||||
export type OnWXResp = (resp: wxopensdk.BaseResp) => void
|
||||
|
||||
const kTag = "WXApiEventHandlerImpl"
|
||||
|
||||
class WXApiEventHandlerImpl implements wxopensdk.WXApiEventHandler {
|
||||
private onReqCallbacks: Map<OnWXReq, OnWXReq> = new Map
|
||||
private onRespCallbacks: Map<OnWXResp, OnWXResp> = new Map
|
||||
|
||||
registerOnWXReqCallback(on: OnWXReq) {
|
||||
this.onReqCallbacks.set(on, on)
|
||||
}
|
||||
unregisterOnWXReqCallback(on: OnWXReq) {
|
||||
this.onReqCallbacks.delete(on)
|
||||
}
|
||||
|
||||
registerOnWXRespCallback(on: OnWXResp) {
|
||||
this.onRespCallbacks.set(on, on)
|
||||
}
|
||||
unregisterOnWXRespCallback(on: OnWXResp) {
|
||||
this.onRespCallbacks.delete(on)
|
||||
}
|
||||
|
||||
onReq(req: wxopensdk.BaseReq): void {
|
||||
wxopensdk.Log.i(kTag, "onReq:%s", JSON.stringify(req))
|
||||
this.onReqCallbacks.forEach((on) => {
|
||||
on(req)
|
||||
})
|
||||
}
|
||||
|
||||
onResp(resp: wxopensdk.BaseResp): void {
|
||||
wxopensdk.Log.i(kTag, "onResp:%s", JSON.stringify(resp))
|
||||
this.onRespCallbacks.forEach((on) => {
|
||||
on(resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const WXApi = wxopensdk.WXAPIFactory.createWXAPI(APP_ID)
|
||||
export const WXEventHandler = new WXApiEventHandlerImpl
|
||||
|
||||
@@ -111,7 +111,7 @@ export class AESEncryptionDecryption {
|
||||
globalResult = AESEncryptionDecryption.uint8ArrayToString(result.data);
|
||||
console.info('解密后的明文:' + globalResult);
|
||||
} catch (err) {
|
||||
console.info(err.message);
|
||||
console.info('解密失败:',err.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BusinessError } from '@ohos.base'
|
||||
import { KeyboardAvoidMode, window } from '@kit.ArkUI'
|
||||
import { resourceManager } from '@kit.LocalizationKit'
|
||||
import { common } from '@kit.AbilityKit'
|
||||
import display from '@ohos.display'
|
||||
|
||||
export class AppUtil {
|
||||
|
||||
@@ -236,6 +237,22 @@ export class AppUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* display width
|
||||
* @returns width in px
|
||||
*/
|
||||
static getDisplayWindowWidth(): Pixels {
|
||||
return px(display.getDefaultDisplaySync().width)
|
||||
}
|
||||
|
||||
/**
|
||||
* display height
|
||||
* @returns height in px
|
||||
*/
|
||||
static getDisplayWindowHeight(): Pixels {
|
||||
return px(display.getDefaultDisplaySync().height)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置沉浸式状态栏
|
||||
* @param isLayoutFullScreen 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、导航栏仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。
|
||||
@@ -342,6 +359,29 @@ export class AppUtil {
|
||||
AppUtil.getContext().terminateSelf();
|
||||
AppUtil.getContext().getApplicationContext().killAllProcesses();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function px(value: number) {
|
||||
return new Pixels(value)
|
||||
}
|
||||
|
||||
export class Pixels {
|
||||
value: number;
|
||||
|
||||
constructor(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
get px() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
get vp() {
|
||||
return px2vp(this.value)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.value}px`
|
||||
}
|
||||
}
|
||||
@@ -315,6 +315,18 @@ export class ChangeUtil {
|
||||
return height
|
||||
}
|
||||
|
||||
static formatPrice(priceStr: string): string {
|
||||
let priceInFen = parseFloat(priceStr);
|
||||
let priceInYuan = priceInFen / 100;
|
||||
return `${priceInYuan.toFixed(2)}`;
|
||||
}
|
||||
|
||||
static formatPrice2(priceStr: string,discount: string): string {
|
||||
let priceInFen = parseFloat(priceStr);
|
||||
let priceInYuan = priceInFen / 100 * parseFloat(discount);
|
||||
return `${priceInYuan.toFixed(2)}`;
|
||||
}
|
||||
|
||||
static Logout(phone:string)
|
||||
{
|
||||
authStore.delUser();
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
import fileIO from '@ohos.fileio';
|
||||
import request from '@ohos.request';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import hilog from '@ohos.hilog';
|
||||
|
||||
/**
|
||||
* 文件下载状态枚举
|
||||
*/
|
||||
export enum DownloadStatus {
|
||||
DOWNLOADING = 'downloading',
|
||||
COMPLETED = 'completed',
|
||||
FAILED = 'failed'
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件信息接口
|
||||
*/
|
||||
export interface FileInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
format: string;
|
||||
size: number;
|
||||
status: DownloadStatus;
|
||||
localPath: string;
|
||||
downloadTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载管理类
|
||||
*/
|
||||
export class FileDownloadManager {
|
||||
private static instance: FileDownloadManager;
|
||||
private downloadQueue: Map<string, FileInfo> = new Map();
|
||||
private downloadTasks: Map<string, request.DownloadTask> = new Map();
|
||||
private context: common.UIAbilityContext;
|
||||
private downloadDir: string = '';
|
||||
|
||||
private constructor(context: common.UIAbilityContext) {
|
||||
this.context = context;
|
||||
this.initDownloadDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static getInstance(context: common.UIAbilityContext): FileDownloadManager {
|
||||
if (!FileDownloadManager.instance) {
|
||||
FileDownloadManager.instance = new FileDownloadManager(context);
|
||||
}
|
||||
return FileDownloadManager.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化下载目录
|
||||
*/
|
||||
private async initDownloadDirectory(): Promise<void> {
|
||||
try {
|
||||
const filesDir = this.context.filesDir;
|
||||
this.downloadDir = `${filesDir}/downloads`;
|
||||
|
||||
// 检查并创建下载目录
|
||||
try {
|
||||
await fileIO.access(this.downloadDir);
|
||||
} catch {
|
||||
await fileIO.mkdir(this.downloadDir);
|
||||
}
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '初始化下载目录失败: %{public}s', String(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始下载文件
|
||||
*/
|
||||
public async downloadFile(url: string,fileId:string, fileName: string, fileType:string): Promise<string> {
|
||||
try {
|
||||
// 从URL中提取文件格式
|
||||
const format = this.extractFileFormat(url);
|
||||
|
||||
// 生成文件名
|
||||
const finalFileName = `${fileName}/${fileId}.${fileType}`;
|
||||
|
||||
// 创建文件信息
|
||||
const fileInfo: FileInfo = {
|
||||
id: fileId,
|
||||
name: finalFileName,
|
||||
url: url,
|
||||
format: format,
|
||||
size: 0,
|
||||
status: DownloadStatus.DOWNLOADING,
|
||||
localPath: `${this.downloadDir}/${finalFileName}`,
|
||||
downloadTime: Date.now()
|
||||
};
|
||||
|
||||
// 添加到下载队列
|
||||
this.downloadQueue.set(fileId, fileInfo);
|
||||
|
||||
// 开始下载
|
||||
await this.startDownload(fileInfo);
|
||||
|
||||
return fileId;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '下载文件失败: %{public}s', String(error));
|
||||
throw new Error(String(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始下载任务
|
||||
*/
|
||||
private async startDownload(fileInfo: FileInfo): Promise<void> {
|
||||
try {
|
||||
const config: request.DownloadConfig = {
|
||||
url: fileInfo.url,
|
||||
title: fileInfo.name,
|
||||
description: '文件下载中...',
|
||||
filePath: fileInfo.localPath,
|
||||
header: {
|
||||
'User-Agent': 'HarmonyOS File Downloader'
|
||||
}
|
||||
};
|
||||
|
||||
const downloadTask = await request.downloadFile(this.context, config);
|
||||
this.downloadTasks.set(fileInfo.id, downloadTask);
|
||||
|
||||
// 监听下载进度
|
||||
downloadTask.on('progress', (receivedSize: number, totalSize: number) => {
|
||||
fileInfo.size = totalSize;
|
||||
this.updateFileInfo(fileInfo);
|
||||
});
|
||||
|
||||
// 监听下载完成
|
||||
downloadTask.on('complete', () => {
|
||||
fileInfo.status = DownloadStatus.COMPLETED;
|
||||
this.updateFileInfo(fileInfo);
|
||||
this.downloadTasks.delete(fileInfo.id);
|
||||
});
|
||||
|
||||
// 监听下载失败
|
||||
downloadTask.on('fail', (err: number) => {
|
||||
fileInfo.status = DownloadStatus.FAILED;
|
||||
this.updateFileInfo(fileInfo);
|
||||
this.downloadTasks.delete(fileInfo.id);
|
||||
hilog.error(0x0000, 'FileDownloadManager', '下载失败: %{public}d', err);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
fileInfo.status = DownloadStatus.FAILED;
|
||||
this.updateFileInfo(fileInfo);
|
||||
throw new Error(String(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文件信息
|
||||
*/
|
||||
private updateFileInfo(fileInfo: FileInfo): void {
|
||||
this.downloadQueue.set(fileInfo.id, fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从URL提取文件格式
|
||||
*/
|
||||
private extractFileFormat(url: string): string {
|
||||
const supportedFormats = ['docx', 'txt', 'ppt', 'pdf', 'doc', 'pptx', 'xls', 'xlsx'];
|
||||
const urlParts = url.split('.');
|
||||
const format = urlParts[urlParts.length - 1].toLowerCase();
|
||||
|
||||
return supportedFormats.includes(format) ? format : 'bin';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载状态
|
||||
*/
|
||||
public getDownloadStatus(fileId: string): DownloadStatus | null {
|
||||
const fileInfo = this.downloadQueue.get(fileId);
|
||||
return fileInfo ? fileInfo.status : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
*/
|
||||
public getFileInfo(fileId: string): FileInfo | null {
|
||||
return this.downloadQueue.get(fileId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有文件信息
|
||||
*/
|
||||
public getAllFiles(): FileInfo[] {
|
||||
return Array.from(this.downloadQueue.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定状态的文件
|
||||
*/
|
||||
public getFilesByStatus(status: DownloadStatus): FileInfo[] {
|
||||
return Array.from(this.downloadQueue.values()).filter(file => file.status === status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看单个文件
|
||||
*/
|
||||
public async viewFile(fileId: string): Promise<boolean> {
|
||||
try {
|
||||
const fileInfo = this.downloadQueue.get(fileId);
|
||||
if (!fileInfo || fileInfo.status !== DownloadStatus.COMPLETED) {
|
||||
throw new Error('文件不存在或下载未完成');
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
try {
|
||||
await fileIO.access(fileInfo.localPath);
|
||||
} catch {
|
||||
throw new Error('文件不存在');
|
||||
}
|
||||
|
||||
// 获取文件信息
|
||||
const stat = await fileIO.stat(fileInfo.localPath);
|
||||
hilog.info(0x0000, 'FileDownloadManager', '文件信息: %{public}s', JSON.stringify(stat));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '查看文件失败: %{public}s', String(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看所有文件
|
||||
*/
|
||||
public async viewAllFiles(): Promise<FileInfo[]> {
|
||||
try {
|
||||
const allFiles = this.getAllFiles();
|
||||
const completedFiles: FileInfo[] = [];
|
||||
|
||||
for (const file of allFiles) {
|
||||
if (file.status === DownloadStatus.COMPLETED) {
|
||||
try {
|
||||
await fileIO.access(file.localPath);
|
||||
completedFiles.push(file);
|
||||
} catch {
|
||||
// 文件不存在,跳过
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return completedFiles;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '查看所有文件失败: %{public}s', String(error));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个文件
|
||||
*/
|
||||
public async deleteFile(fileId: string): Promise<boolean> {
|
||||
try {
|
||||
const fileInfo = this.downloadQueue.get(fileId);
|
||||
if (!fileInfo) {
|
||||
throw new Error('文件不存在');
|
||||
}
|
||||
|
||||
// 取消下载任务
|
||||
const downloadTask = this.downloadTasks.get(fileId);
|
||||
if (downloadTask) {
|
||||
downloadTask.off('progress');
|
||||
downloadTask.off('complete');
|
||||
downloadTask.off('fail');
|
||||
this.downloadTasks.delete(fileId);
|
||||
}
|
||||
|
||||
// 删除本地文件
|
||||
if (fileInfo.status === DownloadStatus.COMPLETED) {
|
||||
try {
|
||||
await fileIO.unlink(fileInfo.localPath);
|
||||
} catch {
|
||||
// 文件可能不存在,忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
// 从队列中移除
|
||||
this.downloadQueue.delete(fileId);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '删除文件失败: %{public}s', String(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除所有文件
|
||||
*/
|
||||
public async deleteAllFiles(): Promise<boolean> {
|
||||
try {
|
||||
const allFiles = Array.from(this.downloadQueue.keys());
|
||||
|
||||
for (const fileId of allFiles) {
|
||||
await this.deleteFile(fileId);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '删除所有文件失败: %{public}s', String(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停下载
|
||||
*/
|
||||
public pauseDownload(fileId: string): boolean {
|
||||
const downloadTask = this.downloadTasks.get(fileId);
|
||||
if (downloadTask) {
|
||||
downloadTask.off('progress');
|
||||
downloadTask.off('complete');
|
||||
downloadTask.off('fail');
|
||||
this.downloadTasks.delete(fileId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复下载
|
||||
*/
|
||||
public async resumeDownload(fileId: string): Promise<boolean> {
|
||||
try {
|
||||
const fileInfo = this.downloadQueue.get(fileId);
|
||||
if (!fileInfo || fileInfo.status !== DownloadStatus.DOWNLOADING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.startDownload(fileInfo);
|
||||
return true;
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, 'FileDownloadManager', '恢复下载失败: %{public}s', String(error));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理下载目录
|
||||
*/
|
||||
// public async cleanDownloadDirectory(): Promise<boolean> {
|
||||
// try {
|
||||
// // 使用fileIO.listDir替代readdir
|
||||
// const files = await fileIO.listDir(this.downloadDir);
|
||||
// for (const file of files) {
|
||||
// const filePath = `${this.downloadDir}/${file}`;
|
||||
// try {
|
||||
// await fileIO.unlink(filePath);
|
||||
// } catch {
|
||||
// // 忽略删除失败的文件
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// } catch (error) {
|
||||
// hilog.error(0x0000, 'FileDownloadManager', '清理下载目录失败: %{public}s', String(error));
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,799 @@
|
||||
import { http } from '@kit.NetworkKit';
|
||||
import { promptAction, router } from '@kit.ArkUI';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { fileIo, fileUri } from '@kit.CoreFileKit';
|
||||
import { FileInfo,DownloadCallback } from '../models/FileModel'
|
||||
import preferences from '@ohos.data.preferences';
|
||||
import { Want } from '@kit.AbilityKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { filePreview } from '@kit.PreviewKit';
|
||||
import request from '@ohos.request';
|
||||
|
||||
// 下载任务接口
|
||||
export interface DownloadTask {
|
||||
httpRequest: http.HttpRequest;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
// 文件统计信息接口
|
||||
export interface FileStat {
|
||||
size: number;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件管理类 - 提供下载、查询、预览等功能
|
||||
*/
|
||||
export class FileManager {
|
||||
private context: common.Context;
|
||||
private downloadTasks: Map<string, DownloadTask> = new Map();
|
||||
private downloadCallbacks: Map<string, DownloadCallback> = new Map();
|
||||
private fileCache: Map<string, FileInfo> = new Map();
|
||||
private downloadDir: string;
|
||||
private isDownloading: boolean = false;
|
||||
private preferencesHelper: preferences.Preferences | null = null;
|
||||
private readonly STORE_NAME = 'file_manager_store';
|
||||
private readonly FILE_INFO_KEY = 'file_info_cache';
|
||||
|
||||
constructor(context: common.Context) {
|
||||
this.context = context;
|
||||
this.downloadDir = `${this.context.filesDir}/downloads`;
|
||||
this.initDownloadDirectory();
|
||||
this.initPreferences();
|
||||
this.loadFileInfoFromStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化偏好设置存储
|
||||
*/
|
||||
private async initPreferences(): Promise<void> {
|
||||
try {
|
||||
this.preferencesHelper = await preferences.getPreferences(this.context, this.STORE_NAME);
|
||||
} catch (error) {
|
||||
console.error('初始化偏好设置失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从存储加载文件信息
|
||||
*/
|
||||
private async loadFileInfoFromStorage(): Promise<void> {
|
||||
try {
|
||||
if (this.preferencesHelper) {
|
||||
const fileInfoJson = await this.preferencesHelper.get(this.FILE_INFO_KEY, '[]');
|
||||
const fileInfoArray: FileInfo[] = JSON.parse(fileInfoJson as string);
|
||||
|
||||
// 恢复缓存
|
||||
for (const fileInfo of fileInfoArray) {
|
||||
this.fileCache.set(fileInfo.fileId, fileInfo);
|
||||
}
|
||||
|
||||
console.info(`从存储加载了 ${fileInfoArray.length} 个文件信息`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载文件信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件信息到存储
|
||||
*/
|
||||
private async saveFileInfoToStorage(): Promise<void> {
|
||||
try {
|
||||
if (this.preferencesHelper) {
|
||||
const fileInfoArray = Array.from(this.fileCache.values());
|
||||
const fileInfoJson = JSON.stringify(fileInfoArray);
|
||||
await this.preferencesHelper.put(this.FILE_INFO_KEY, fileInfoJson);
|
||||
await this.preferencesHelper.flush();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存文件信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化下载目录
|
||||
*/
|
||||
private async initDownloadDirectory(): Promise<void> {
|
||||
try {
|
||||
if (!fileIo.accessSync(this.downloadDir)) {
|
||||
fileIo.mkdirSync(this.downloadDir);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建下载目录失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param fileInfo 文件信息
|
||||
* @param callback 下载回调
|
||||
* @returns Promise<boolean>
|
||||
*/
|
||||
async downloadFile(fileInfo: FileInfo, callback?: DownloadCallback): Promise<boolean> {
|
||||
try {
|
||||
// 检查是否正在下载
|
||||
if (this.downloadTasks.has(fileInfo.fileId)) {
|
||||
promptAction.showToast({ message: '文件正在下载中,请勿重复操作', duration: 2000 });
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查文件是否已下载
|
||||
if (await this.isFileDownloaded(fileInfo.fileId)) {
|
||||
promptAction.showToast({ message: '文件已下载', duration: 2000 });
|
||||
return true;
|
||||
}
|
||||
|
||||
// 设置下载状态
|
||||
this.isDownloading = true;
|
||||
fileInfo.downloadStatus = 'downloading';
|
||||
fileInfo.downloadProgress = 0;
|
||||
|
||||
// 保存回调
|
||||
if (callback) {
|
||||
this.downloadCallbacks.set(fileInfo.fileId, callback);
|
||||
}
|
||||
|
||||
// 创建下载任务
|
||||
const downloadTask = await this.createDownloadTask(fileInfo);
|
||||
this.downloadTasks.set(fileInfo.fileId, downloadTask);
|
||||
|
||||
// 开始下载
|
||||
const success = await this.startDownload(downloadTask, fileInfo);
|
||||
|
||||
if (success) {
|
||||
fileInfo.downloadStatus = 'downloaded';
|
||||
fileInfo.downloadProgress = 100;
|
||||
this.onDownloadSuccess(fileInfo);
|
||||
} else {
|
||||
fileInfo.downloadStatus = 'failed';
|
||||
this.onDownloadFailed(fileInfo, '下载失败');
|
||||
}
|
||||
|
||||
return success;
|
||||
} catch (error) {
|
||||
console.error('下载文件失败:', error);
|
||||
fileInfo.downloadStatus = 'failed';
|
||||
this.onDownloadFailed(fileInfo, '下载失败');
|
||||
return false;
|
||||
} finally {
|
||||
this.isDownloading = false;
|
||||
this.downloadTasks.delete(fileInfo.fileId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建下载任务
|
||||
*/
|
||||
private async createDownloadTask(fileInfo: FileInfo): Promise<DownloadTask> {
|
||||
try {
|
||||
// 创建文件名目录
|
||||
const fileNameDir = `${fileInfo.fileName}`;
|
||||
const fileNameDirPath = `${this.downloadDir}/${fileNameDir}`;
|
||||
|
||||
// 确保目录存在
|
||||
if (!fileIo.accessSync(fileNameDirPath)) {
|
||||
fileIo.mkdirSync(fileNameDirPath);
|
||||
}
|
||||
|
||||
// 设置文件保存路径 - 格式:文件名/文件ID.文件格式
|
||||
const fileName = `${fileInfo.fileId}.${fileInfo.fileType}`;
|
||||
const filePath = `${fileNameDirPath}/${fileName}`;
|
||||
fileInfo.filePath = filePath;
|
||||
|
||||
// 创建HTTP请求
|
||||
const httpRequest = http.createHttp();
|
||||
|
||||
const task: DownloadTask = {
|
||||
httpRequest: httpRequest,
|
||||
filePath: filePath
|
||||
};
|
||||
|
||||
return task;
|
||||
} catch (error) {
|
||||
console.error('创建下载任务失败:', error);
|
||||
throw new Error('创建下载任务失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始下载
|
||||
*/
|
||||
private async startDownload(downloadTask: DownloadTask, fileInfo: FileInfo): Promise<boolean> {
|
||||
//
|
||||
// let contexta = getContext(this) as common.UIAbilityContext;
|
||||
// request.downloadFile(contexta.getApplicationContext(), {
|
||||
// url: fileInfo.fileUrl,
|
||||
// filePath: downloadTask.filePath,
|
||||
// enableMetered:true
|
||||
// }).then((downloadTask: request.DownloadTask) => {
|
||||
// let progresCallback = (receivedSize: number, totalSize: number) => {
|
||||
// console.info("downloadddd1 receivedSize:" + receivedSize + " totalSize:" + totalSize);
|
||||
// };
|
||||
// let pauseCallback = () => {
|
||||
// console.info('Downloadddd task pause.');
|
||||
// };
|
||||
// //开启进度回调
|
||||
// downloadTask.on('progress', progresCallback);
|
||||
// //开启暂停回调
|
||||
// downloadTask.on('pause', pauseCallback);
|
||||
// //开启下载完成回调
|
||||
// downloadTask.on('complete', async () => {
|
||||
// const taskInfo = await downloadTask.getTaskInfo();
|
||||
// console.info('downloaddddTask1 complete:'+`status: ${taskInfo.status}`);
|
||||
// // downloadTask.getTaskInfo();
|
||||
//
|
||||
// })
|
||||
// return true
|
||||
// }).catch((err: BusinessError) => {
|
||||
// console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
|
||||
// return false;
|
||||
// })
|
||||
try {
|
||||
const httpRequest = downloadTask.httpRequest;
|
||||
const filePath = downloadTask.filePath;
|
||||
|
||||
// 配置下载选项 - 参考iOS代码的请求头设置
|
||||
const options: http.HttpRequestOptions = {
|
||||
method: http.RequestMethod.GET,
|
||||
header: {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HarmonyOS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||
},
|
||||
expectDataType: http.HttpDataType.ARRAY_BUFFER,
|
||||
usingCache: false,
|
||||
connectTimeout: 60000,
|
||||
readTimeout: 300000
|
||||
};
|
||||
|
||||
console.info(`开始下载文件: ${fileInfo.fileUrl}`);
|
||||
console.info(`目标路径: ${filePath}`);
|
||||
|
||||
// 发起下载请求
|
||||
const response = await httpRequest.request(fileInfo.fileUrl, options);
|
||||
|
||||
console.info(`响应状态码: ${response.responseCode}`);
|
||||
console.info(`响应头: ${JSON.stringify(response.header)}`);
|
||||
|
||||
if (response.responseCode === 200) {
|
||||
try {
|
||||
// 验证响应数据
|
||||
if (!response.result) {
|
||||
console.error('下载响应数据为空');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查数据类型和大小
|
||||
if (!(response.result instanceof ArrayBuffer)) {
|
||||
console.error('响应数据不是ArrayBuffer类型:', typeof response.result);
|
||||
return false;
|
||||
}
|
||||
|
||||
const dataSize = response.result.byteLength;
|
||||
console.info(`下载数据大小: ${dataSize} 字节`);
|
||||
|
||||
if (dataSize <= 10) {
|
||||
console.error('文件有误:',response.result)
|
||||
return false
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
|
||||
if (!fileIo.accessSync(dirPath)) {
|
||||
fileIo.mkdirSync(dirPath);
|
||||
}
|
||||
|
||||
// 将下载的数据写入文件
|
||||
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
|
||||
|
||||
try {
|
||||
// 写入文件数据
|
||||
const bytesWritten = fileIo.writeSync(file.fd, response.result);
|
||||
console.info(`写入文件成功,写入字节数: ${bytesWritten}`);
|
||||
|
||||
// 验证写入的字节数
|
||||
if (bytesWritten !== dataSize) {
|
||||
console.error(`写入字节数不匹配: 期望 ${dataSize}, 实际 ${bytesWritten}`);
|
||||
fileIo.closeSync(file);
|
||||
fileIo.unlinkSync(filePath); // 删除损坏的文件
|
||||
return false;
|
||||
}
|
||||
|
||||
// 同步文件到磁盘
|
||||
fileIo.fsyncSync(file.fd);
|
||||
|
||||
// 关闭文件
|
||||
fileIo.closeSync(file);
|
||||
|
||||
// 验证文件是否创建成功
|
||||
if (!fileIo.accessSync(filePath)) {
|
||||
console.error('文件创建失败');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取文件统计信息
|
||||
const stat = fileIo.statSync(filePath);
|
||||
console.info(`文件创建成功: ${filePath}, 大小: ${stat.size} 字节`);
|
||||
|
||||
// 验证文件大小
|
||||
if (stat.size !== dataSize) {
|
||||
console.error(`文件大小不匹配: 期望 ${dataSize}, 实际 ${stat.size}`);
|
||||
fileIo.unlinkSync(filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新文件信息
|
||||
fileInfo.fileSize = stat.size;
|
||||
await this.saveFileInfo(fileInfo);
|
||||
|
||||
return true;
|
||||
} catch (writeError) {
|
||||
console.error('写入文件失败:', writeError);
|
||||
fileIo.closeSync(file);
|
||||
// 清理可能创建的文件
|
||||
try {
|
||||
if (fileIo.accessSync(filePath)) {
|
||||
fileIo.unlinkSync(filePath);
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
console.error('清理文件失败:', cleanupError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} catch (writeError) {
|
||||
console.error('文件写入过程失败:', writeError);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.error(`下载失败,状态码: ${response.responseCode}`);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('下载执行失败:', error);
|
||||
return false;
|
||||
} finally {
|
||||
// 销毁HTTP请求
|
||||
downloadTask.httpRequest.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载
|
||||
*/
|
||||
async cancelDownload(fileId: string): Promise<boolean> {
|
||||
try {
|
||||
const task = this.downloadTasks.get(fileId);
|
||||
if (task) {
|
||||
// 销毁HTTP请求
|
||||
task.httpRequest.destroy();
|
||||
this.downloadTasks.delete(fileId);
|
||||
|
||||
// 更新状态
|
||||
const fileInfo = this.fileCache.get(fileId);
|
||||
if (fileInfo) {
|
||||
fileInfo.downloadStatus = 'cancelled';
|
||||
fileInfo.downloadProgress = 0;
|
||||
this.onDownloadCancelled(fileInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('取消下载失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已下载文件
|
||||
*/
|
||||
async getDownloadedFiles(): Promise<FileInfo[]> {
|
||||
try {
|
||||
const files: FileInfo[] = [];
|
||||
|
||||
// 从缓存获取
|
||||
for (const fileInfo of this.fileCache.values()) {
|
||||
if (fileInfo.downloadStatus === 'downloaded') {
|
||||
files.push(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// 从本地目录扫描
|
||||
const localFiles = await this.scanLocalFiles();
|
||||
files.push(...localFiles);
|
||||
|
||||
// 按时间排序
|
||||
return files.sort((a, b) => b.updateTime - a.updateTime);
|
||||
} catch (error) {
|
||||
console.error('查询下载文件失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描本地文件
|
||||
*/
|
||||
private async scanLocalFiles(): Promise<FileInfo[]> {
|
||||
try {
|
||||
const files: FileInfo[] = [];
|
||||
|
||||
if (!fileIo.accessSync(this.downloadDir)) {
|
||||
return files;
|
||||
}
|
||||
|
||||
const entryList = fileIo.listFileSync(this.downloadDir);
|
||||
|
||||
for (let i = 0; i < entryList.length; i++) {
|
||||
const entryName = entryList[i];
|
||||
const entryPath = `${this.downloadDir}/${entryName}`;
|
||||
|
||||
// 优先按目录处理(当前下载结构是 按文件名建目录,目录下放置 文件ID.扩展名)
|
||||
let childNames: string[] | null = null;
|
||||
try {
|
||||
childNames = fileIo.listFileSync(entryPath);
|
||||
} catch (_e) {
|
||||
childNames = null;
|
||||
}
|
||||
|
||||
if (childNames && childNames.length > 0) {
|
||||
// 目录:遍历子文件
|
||||
for (let j = 0; j < childNames.length; j++) {
|
||||
const childName = childNames[j];
|
||||
const childPath = `${entryPath}/${childName}`;
|
||||
try {
|
||||
const childStat = fileIo.statSync(childPath);
|
||||
const fileInfo = this.parseFileName(childName, childPath, childStat);
|
||||
if (fileInfo) {
|
||||
files.push(fileInfo);
|
||||
}
|
||||
} catch (childErr) {
|
||||
console.error('读取子文件失败:', childErr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 非目录项(兼容旧结构,直接作为文件解析)
|
||||
try {
|
||||
const stat = fileIo.statSync(entryPath);
|
||||
const fileInfo = this.parseFileName(entryName, entryPath, stat);
|
||||
if (fileInfo) {
|
||||
files.push(fileInfo);
|
||||
}
|
||||
} catch (fileErr) {
|
||||
console.error('读取文件失败:', fileErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
} catch (error) {
|
||||
console.error('扫描本地文件失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件名
|
||||
*/
|
||||
private parseFileName(fileName: string, filePath: string, stat: fileIo.Stat): FileInfo | null {
|
||||
try {
|
||||
// 新的文件名格式: 文件名/文件ID.扩展名
|
||||
const pathParts = filePath.split('/');
|
||||
if (pathParts.length < 2) return null;
|
||||
|
||||
const fileNameDir = pathParts[pathParts.length - 2]; // 文件名目录
|
||||
const fileNameWithExt = pathParts[pathParts.length - 1]; // 文件ID.扩展名
|
||||
|
||||
const fileIdParts = fileNameWithExt.split('.');
|
||||
if (fileIdParts.length < 2) return null;
|
||||
|
||||
const fileId = fileIdParts[0];
|
||||
const extension = fileIdParts[1];
|
||||
|
||||
return {
|
||||
fileId,
|
||||
fileName: fileNameDir, // 使用目录名作为文件名
|
||||
fileType: extension,
|
||||
fileSize: stat.size,
|
||||
fileUrl: '',
|
||||
filePath: filePath,
|
||||
downloadStatus: 'downloaded',
|
||||
downloadProgress: 100,
|
||||
createTime: stat.ctime,
|
||||
updateTime: stat.mtime
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('解析文件名失败:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否已下载
|
||||
*/
|
||||
private async isFileDownloaded(fileId: string): Promise<boolean> {
|
||||
try {
|
||||
// 检查缓存
|
||||
const cachedFile = this.fileCache.get(fileId);
|
||||
if (cachedFile && cachedFile.downloadStatus === 'downloaded') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查本地文件
|
||||
const localFiles = await this.scanLocalFiles();
|
||||
for (let i = 0; i < localFiles.length; i++) {
|
||||
if (localFiles[i].fileId === fileId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('检查文件下载状态失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览文件 - 使用系统原生方法
|
||||
*/
|
||||
async previewFile(identifier: string) {
|
||||
try {
|
||||
// 1) 优先按 uuid 在缓存中查找
|
||||
let target: FileInfo | null = null;
|
||||
const cachedById = this.fileCache.get(identifier);
|
||||
if (cachedById) {
|
||||
target = cachedById;
|
||||
}
|
||||
|
||||
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
|
||||
if (!target) {
|
||||
let latest: FileInfo | null = null;
|
||||
for (const info of this.fileCache.values()) {
|
||||
if (info.fileName === identifier) {
|
||||
if (!latest || info.updateTime > latest.updateTime) {
|
||||
latest = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = latest;
|
||||
}
|
||||
|
||||
// 3) 若缓存仍未命中,扫描本地目录
|
||||
if (!target) {
|
||||
const localFiles = await this.scanLocalFiles();
|
||||
let latest: FileInfo | null = null;
|
||||
for (const info of localFiles) {
|
||||
if (info.fileId === identifier || info.fileName === identifier) {
|
||||
if (!latest || info.updateTime > latest.updateTime) {
|
||||
latest = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = latest;
|
||||
}
|
||||
|
||||
if (!target || !target.filePath) {
|
||||
promptAction.showToast({ message: '未找到对应文件', duration: 2000 });
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = target.filePath;
|
||||
const fileType = target.fileType || '';
|
||||
|
||||
// 文件是否存在
|
||||
if (!fileIo.accessSync(filePath)) {
|
||||
promptAction.showToast({ message: '文件不存在', duration: 2000 });
|
||||
return;
|
||||
}
|
||||
|
||||
let uri = fileUri.getUriFromPath(filePath);
|
||||
filePreview.canPreview(this.context, uri).then((result) => { // 传入支持的文件类型且项目存在时会返回true
|
||||
console.info(`Succeeded in obtaining the result of whether it can be previewed. result = ${result}`);
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(`Failed to obtain the result of whether it can be previewed, err.code = ${err.code}, err.message = ${err.message}`);
|
||||
});
|
||||
|
||||
let fileInfo: filePreview.PreviewInfo = {
|
||||
title: target.fileName,
|
||||
uri: uri,
|
||||
mimeType: this.getMimeType(fileType)
|
||||
};
|
||||
let files: Array<filePreview.PreviewInfo> = new Array();
|
||||
files.push(fileInfo);
|
||||
filePreview.openPreview(this.context, files, 0).then(() => {
|
||||
console.info('Succeeded in opening preview');
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(`Failed to open preview, err.code = ${err.code}, err.message = ${err.message}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('打开文件异常:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MIME类型
|
||||
*/
|
||||
private getMimeType(fileType: string): string {
|
||||
const mimeTypes: Record<string, string> = {
|
||||
'pdf': 'application/pdf',
|
||||
'doc': 'application/msword',
|
||||
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'ppt': 'application/vnd.ms-powerpoint',
|
||||
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'xls': 'application/vnd.ms-excel',
|
||||
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'txt': 'text/plain',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'bmp': 'image/bmp',
|
||||
'mp4': 'video/mp4',
|
||||
'avi': 'video/x-msvideo',
|
||||
'mov': 'video/quicktime',
|
||||
'wmv': 'video/x-ms-wmv'
|
||||
};
|
||||
return mimeTypes[fileType.toLowerCase()] || '*/*';
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
async deleteFile(identifier: string): Promise<boolean> {
|
||||
try {
|
||||
// 1) 优先按 fileId 在缓存中查找
|
||||
let target: FileInfo | null = null;
|
||||
const cachedById = this.fileCache.get(identifier);
|
||||
if (cachedById) {
|
||||
target = cachedById;
|
||||
}
|
||||
|
||||
// 2) 若未命中,再按文件名在缓存中查找(取最近更新)
|
||||
if (!target) {
|
||||
let latest: FileInfo | null = null;
|
||||
for (const info of this.fileCache.values()) {
|
||||
if (info.fileName === identifier) {
|
||||
if (!latest || info.updateTime > latest.updateTime) {
|
||||
latest = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = latest;
|
||||
}
|
||||
|
||||
// 3) 若缓存仍未命中,扫描本地目录
|
||||
if (!target) {
|
||||
const localFiles = await this.scanLocalFiles();
|
||||
let latest: FileInfo | null = null;
|
||||
for (const info of localFiles) {
|
||||
if (info.fileId === identifier || info.fileName === identifier) {
|
||||
if (!latest || info.updateTime > latest.updateTime) {
|
||||
latest = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = latest;
|
||||
}
|
||||
|
||||
if (!target || !target.filePath) {
|
||||
console.error('未找到要删除的文件:', identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 删除本地文件
|
||||
if (fileIo.accessSync(target.filePath)) {
|
||||
fileIo.unlinkSync(target.filePath);
|
||||
console.info(`成功删除文件: ${target.filePath}`);
|
||||
|
||||
// 删除文件后,检查并删除空的文件夹
|
||||
await this.cleanupEmptyDirectory(target.filePath);
|
||||
}
|
||||
|
||||
// 从缓存中移除
|
||||
this.fileCache.delete(target.fileId);
|
||||
|
||||
// 更新持久化存储
|
||||
await this.saveFileInfoToStorage();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('删除文件失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理空文件夹
|
||||
*/
|
||||
private async cleanupEmptyDirectory(filePath: string): Promise<void> {
|
||||
try {
|
||||
// 获取文件所在目录
|
||||
const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
|
||||
|
||||
// 检查目录是否存在
|
||||
if (!fileIo.accessSync(dirPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查目录是否为空
|
||||
const files = fileIo.listFileSync(dirPath);
|
||||
if (files.length === 0) {
|
||||
// 目录为空,删除目录
|
||||
fileIo.rmdirSync(dirPath);
|
||||
console.info(`成功删除空文件夹: ${dirPath}`);
|
||||
|
||||
// 递归检查父目录是否也为空(但不要删除下载根目录)
|
||||
const parentDir = dirPath.substring(0, dirPath.lastIndexOf('/'));
|
||||
if (parentDir !== this.downloadDir && fileIo.accessSync(parentDir)) {
|
||||
const parentFiles = fileIo.listFileSync(parentDir);
|
||||
if (parentFiles.length === 0) {
|
||||
fileIo.rmdirSync(parentDir);
|
||||
console.info(`成功删除空父文件夹: ${parentDir}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('清理空文件夹失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载状态
|
||||
*/
|
||||
isDownloadingFile(): boolean {
|
||||
return this.isDownloading;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件信息
|
||||
*/
|
||||
private async saveFileInfo(fileInfo: FileInfo): Promise<void> {
|
||||
try {
|
||||
this.fileCache.set(fileInfo.fileId, fileInfo);
|
||||
// 保存到持久化存储
|
||||
await this.saveFileInfoToStorage();
|
||||
} catch (error) {
|
||||
console.error('保存文件信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载成功回调
|
||||
*/
|
||||
private onDownloadSuccess(fileInfo: FileInfo): void {
|
||||
const callback = this.downloadCallbacks.get(fileInfo.fileId);
|
||||
if (callback && callback.onSuccess) {
|
||||
callback.onSuccess(fileInfo.filePath || '');
|
||||
}
|
||||
this.downloadCallbacks.delete(fileInfo.fileId);
|
||||
|
||||
promptAction.showToast({ message: '下载完成', duration: 2000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载失败回调
|
||||
*/
|
||||
private onDownloadFailed(fileInfo: FileInfo, error: string): void {
|
||||
const callback = this.downloadCallbacks.get(fileInfo.fileId);
|
||||
if (callback && callback.onFailed) {
|
||||
callback.onFailed(error);
|
||||
}
|
||||
this.downloadCallbacks.delete(fileInfo.fileId);
|
||||
|
||||
promptAction.showToast({ message: error, duration: 2000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载取消回调
|
||||
*/
|
||||
private onDownloadCancelled(fileInfo: FileInfo): void {
|
||||
const callback = this.downloadCallbacks.get(fileInfo.fileId);
|
||||
if (callback && callback.onCancelled) {
|
||||
callback.onCancelled();
|
||||
}
|
||||
this.downloadCallbacks.delete(fileInfo.fileId);
|
||||
|
||||
promptAction.showToast({ message: '下载已取消', duration: 2000 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
// 导入必要的鸿蒙模块
|
||||
import http from '@ohos.net.http';
|
||||
import fs from '@ohos.file.fs';
|
||||
import relationalStore from '@ohos.data.relationalStore';
|
||||
import common from '@ohos.app.ability.common';
|
||||
import security from '@ohos.security.cryptoFramework';
|
||||
import util from '@ohos.util';
|
||||
import { BusinessError } from '@ohos.base';
|
||||
import promptAction from '@ohos.promptAction';
|
||||
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
|
||||
import { authStore } from './auth';
|
||||
import { AESEncryptionDecryption } from './AESEncryptionDecryption';
|
||||
import { BasicConstant } from '../constants/BasicConstant';
|
||||
import { rcp } from '@kit.RemoteCommunicationKit';
|
||||
import fileIo from '@ohos.file.fs';
|
||||
|
||||
// 1. 定义数据模型和数据库结构
|
||||
// 文件信息实体
|
||||
class WPSFile {
|
||||
fileId: string = ''; // 对应fileID
|
||||
fileTitle: string = ''; // 对应fileTitle
|
||||
fileType: string = ''; // 对应fileType
|
||||
filePath: string = ''; // 本地存储路径
|
||||
downloadType: string = ''; // 下载状态: 'downloading', 'downloadFalse', 'downloaded'
|
||||
md5?: string = ''; // 文件MD5值
|
||||
}
|
||||
|
||||
// 数据库管理器
|
||||
export class DBManager {
|
||||
private rdbStore: relationalStore.RdbStore | null = null;
|
||||
private readonly TABLE_NAME: string = 'WPSFILELIST';
|
||||
private readonly STORE_CONFIG: relationalStore.StoreConfig = {
|
||||
name: 'WPSFileDB.db',
|
||||
securityLevel: relationalStore.SecurityLevel.S1,
|
||||
};
|
||||
private context: common.Context;
|
||||
|
||||
constructor(context: common.Context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
// 初始化数据库
|
||||
async initializeDB(): Promise<void> {
|
||||
try {
|
||||
this.rdbStore = await relationalStore.getRdbStore(this.context, this.STORE_CONFIG);
|
||||
// 创建表
|
||||
const sql = `
|
||||
CREATE TABLE IF NOT EXISTS ${this.TABLE_NAME} (
|
||||
fileId TEXT PRIMARY KEY,
|
||||
fileTitle TEXT NOT NULL,
|
||||
fileType TEXT NOT NULL,
|
||||
filePath TEXT,
|
||||
downloadType TEXT NOT NULL,
|
||||
md5 TEXT
|
||||
)`;
|
||||
await this.rdbStore.executeSql(sql);
|
||||
console.log('数据库初始化成功');
|
||||
} catch (err) {
|
||||
console.error(`数据库初始化失败: ${err.code}, ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 插入文件记录
|
||||
async insertFile(file: WPSFile): Promise<boolean> {
|
||||
if (!this.rdbStore) {
|
||||
await this.initializeDB();
|
||||
}
|
||||
|
||||
try {
|
||||
const valueBucket: relationalStore.ValuesBucket = {
|
||||
'fileId': file.fileId,
|
||||
'fileTitle': file.fileTitle,
|
||||
'fileType': file.fileType,
|
||||
'filePath': file.filePath,
|
||||
'downloadType': file.downloadType,
|
||||
'md5': file.md5 || ''
|
||||
};
|
||||
|
||||
await this.rdbStore?.insert(this.TABLE_NAME, valueBucket);
|
||||
console.log('文件记录插入成功');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(`插入文件记录失败: ${err.code}, ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新下载状态
|
||||
async updateDownloadStatus(fileId: string, status: string): Promise<boolean> {
|
||||
if (!this.rdbStore) {
|
||||
await this.initializeDB();
|
||||
}
|
||||
|
||||
try {
|
||||
const valueBucket: relationalStore.ValuesBucket = {
|
||||
'downloadType': status
|
||||
};
|
||||
|
||||
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
|
||||
predicates.equalTo('fileId', fileId);
|
||||
|
||||
await this.rdbStore?.update(valueBucket, predicates);
|
||||
console.log('下载状态更新成功');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(`更新下载状态失败: ${err.code}, ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件记录
|
||||
async deleteFile(fileId: string): Promise<boolean> {
|
||||
if (!this.rdbStore) {
|
||||
await this.initializeDB();
|
||||
}
|
||||
|
||||
try {
|
||||
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
|
||||
predicates.equalTo('fileId', fileId);
|
||||
|
||||
await this.rdbStore?.delete(predicates);
|
||||
console.log('文件记录删除成功');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(`删除文件记录失败: ${err.code}, ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据ID查询文件
|
||||
async getFileById(fileId: string): Promise<WPSFile | null> {
|
||||
if (!this.rdbStore) {
|
||||
await this.initializeDB();
|
||||
}
|
||||
|
||||
try {
|
||||
let predicates: relationalStore.RdbPredicates = new relationalStore.RdbPredicates(this.TABLE_NAME);
|
||||
predicates.equalTo('fileId', fileId);
|
||||
|
||||
let resultSet = await this.rdbStore?.query(predicates, ['fileId', 'fileTitle', 'fileType', 'filePath', 'downloadType', 'md5']);
|
||||
if (resultSet && resultSet.rowCount > 0) {
|
||||
await resultSet.goToFirstRow();
|
||||
let file = new WPSFile();
|
||||
file.fileId = resultSet.getString(resultSet.getColumnIndex('fileId')) || '';
|
||||
file.fileTitle = resultSet.getString(resultSet.getColumnIndex('fileTitle')) || '';
|
||||
file.fileType = resultSet.getString(resultSet.getColumnIndex('fileType')) || '';
|
||||
file.filePath = resultSet.getString(resultSet.getColumnIndex('filePath')) || '';
|
||||
file.downloadType = resultSet.getString(resultSet.getColumnIndex('downloadType')) || '';
|
||||
file.md5 = resultSet.getString(resultSet.getColumnIndex('md5')) || '';
|
||||
resultSet.close();
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
console.error(`查询文件失败: ${err.code}, ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 计算文件 MD5 (使用路径创建流)
|
||||
async function calculateFileMD5(filePath: string): Promise<string> {
|
||||
let stream: fs.Stream = fs.createStreamSync(filePath, 'r');
|
||||
let md5AlgName: string = "MD5";
|
||||
let hash: cryptoFramework.Md = cryptoFramework.createMd(md5AlgName);
|
||||
|
||||
let dataBuff: ArrayBuffer = new ArrayBuffer(4096);
|
||||
let readCount: number = stream.readSync(dataBuff);
|
||||
while (readCount > 0) {
|
||||
let messageData: Uint8Array = new Uint8Array(dataBuff.slice(0, readCount));
|
||||
let updateMessageBlob: cryptoFramework.DataBlob = { data: messageData };
|
||||
hash.updateSync(updateMessageBlob);
|
||||
readCount = stream.readSync(dataBuff);
|
||||
}
|
||||
stream.closeSync();
|
||||
|
||||
let md5Result: cryptoFramework.DataBlob = hash.digestSync();
|
||||
let md5Hex: string = Array.from(md5Result.data).map(byte => byte.toString(16).padStart(2, '0')).join('');
|
||||
return md5Hex;
|
||||
}
|
||||
|
||||
// 2. 文件下载管理器
|
||||
export class FileManagerCopy {
|
||||
private httpRequest: http.HttpRequest = http.createHttp();
|
||||
private context: common.Context;
|
||||
private dbManager: DBManager;
|
||||
|
||||
constructor(context: common.Context) {
|
||||
this.context = context;
|
||||
this.dbManager = new DBManager(context);
|
||||
}
|
||||
|
||||
private generateRandomString(): string {
|
||||
// 生成1到10之间的随机整数作为字符串长度[6,8](@ref)
|
||||
const minLength: number = 1;
|
||||
const maxLength: number = 10;
|
||||
const targetLength: number = Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;
|
||||
// 初始化一个空数组用于存储字符
|
||||
let charArray: string[] = [];
|
||||
// 循环生成指定数量的随机大写字母[6](@ref)
|
||||
for (let i = 0; i < targetLength; i++) {
|
||||
// 生成65('A')到90('Z')之间的随机数[6](@ref)
|
||||
const randomCharCode: number = Math.floor(Math.random() * 26) + 65;
|
||||
// 将Unicode编码转换为字符并添加到数组
|
||||
charArray.push(String.fromCharCode(randomCharCode));
|
||||
}
|
||||
return charArray.join('');
|
||||
}
|
||||
|
||||
async downloadFile(wpsUuid: string, wpsTitle: string, wpsType: string, fileMd5: string, order_id:string , orderStatus: number, timestamp: string) {
|
||||
// 检查是否正在下载
|
||||
let existingFile = await this.dbManager.getFileById(wpsUuid);
|
||||
if (existingFile && existingFile.downloadType === 'downloading') {
|
||||
promptAction.showToast({ message: '正在下载,请勿重复点击', duration: 2000 });
|
||||
// return false;
|
||||
}
|
||||
|
||||
let daijiami:string = ''
|
||||
if (orderStatus == 1) {//免费
|
||||
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
|
||||
}
|
||||
// else if (orderStatus == 8) {//复制iOS代码逻辑,有这样的内容
|
||||
// daijiami = `${wpsUuid}|${orderStatus}|${authStore.getUser().uuid}|${timestamp}`
|
||||
// }
|
||||
else {
|
||||
daijiami = `${wpsUuid}|${order_id}|${authStore.getUser().uuid}|${timestamp}`
|
||||
}
|
||||
// f6f25104fa0345b38074710c9356948b|USEWELFARENUM|1CBMDQbuOX3xbxAcxE5|2025-09-01 11:02:42
|
||||
//ios-//f6f25104fa0345b38074710c9356948b|USEWELFARENUM|GA5LeMOXChsKxMrqFnL|2025-08-29 17:25:54
|
||||
const scanData = await AESEncryptionDecryption.aesEncrypt(daijiami,BasicConstant.ExpertAesKey)
|
||||
//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS/916uiWz8iNWCZMYLa4iKXw3
|
||||
const encodedString = encodeURIComponent(scanData)
|
||||
// hGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F916uiWz8iNWCZMYLa4iKXw3
|
||||
//ios-//hGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
|
||||
let pinString = 'X'+encodedString
|
||||
//XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP
|
||||
//ios-//XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv
|
||||
const targetLength: number = Math.floor(Math.random() * (10 - 1 + 1)) + 1;
|
||||
let downloadUrl = `${BasicConstant.urlExpertApp}downloadGanDanFile?&gdf=${pinString}&a=${targetLength}`
|
||||
//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao%2BF44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY5jF7eBABjSJDjIJL7uvAusvVMJT9371Bvey44xs48MtAaBPRdKMufmGMUkiOiS%2F90X6zMHwPph2jDuPjTMzXqP&a=10
|
||||
//ios-//https://dev-app.igandan.com/app/expertApp/downloadGanDanFile?&gdf=XhGaao+F44L7qAHJW3s6SuuJHMpP5jrLrFbPRA6AuJY7QHyYsqbbyOz0oyH4orYtJgPbG1eN9syk0G2vJ1oxq9/0V/ZZMHwb7Qu6H1TEVKLsZOInUm1rOJBhY1/hac4Dv&a=1
|
||||
|
||||
// 创建下载记录
|
||||
let wpsFile = new WPSFile();
|
||||
wpsFile.fileId = wpsUuid;
|
||||
wpsFile.fileTitle = wpsTitle;
|
||||
wpsFile.fileType = wpsType;
|
||||
wpsFile.downloadType = 'downloading';
|
||||
wpsFile.md5 = fileMd5;
|
||||
|
||||
//下载文件保存的文件夹路径,仅为示例,请按需求进行替换。
|
||||
const DOWNLOAD_TO_PATH = `/data/storage/el2/base/haps/entry/files`;
|
||||
// 创建了一个安全配置对象,其中remoteValidation设置为'skip',表示将跳过远程验证。
|
||||
const securityConfig: rcp.SecurityConfiguration = {
|
||||
remoteValidation: 'skip'
|
||||
}
|
||||
|
||||
// 创建了一个下载配置对象,其中kind设置为'folder',表示下载的目标是文件夹,path设置为之前定义的DOWNLOAD_TO_PATH。
|
||||
let downloadToFile: rcp.DownloadToFile = {
|
||||
kind: 'folder',
|
||||
path: DOWNLOAD_TO_PATH
|
||||
}
|
||||
|
||||
// 创建一个HTTP会话,其中请求配置包括传输超时设置和安全配置(配置可自定义)
|
||||
const session = rcp.createSession({
|
||||
requestConfiguration: {
|
||||
transfer: { timeout: { connectMs: 6000, transferMs: 6000, inactivityMs: 6000 } },
|
||||
security: securityConfig
|
||||
}
|
||||
})
|
||||
|
||||
// 检查目标路径是否存在
|
||||
if (fileIo.accessSync(DOWNLOAD_TO_PATH)) {
|
||||
fileIo.rmdirSync(DOWNLOAD_TO_PATH);
|
||||
}
|
||||
// 发起请求,执行下载操作
|
||||
session.downloadToFile(downloadUrl, downloadToFile)
|
||||
.then((response: rcp.Response) => {
|
||||
console.info(`Successfully received the response, statusCode: ${JSON.stringify(response.statusCode)}`);
|
||||
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(`Failed, the error message is ${JSON.stringify(err)}`)
|
||||
|
||||
})
|
||||
|
||||
// // 文件保存路径
|
||||
// let filesDir = this.context.filesDir;
|
||||
// wpsFile.filePath = `${filesDir}/${wpsUuid}.${wpsType}`;
|
||||
//
|
||||
// // 插入数据库记录
|
||||
// let insertSuccess = await this.dbManager.insertFile(wpsFile);
|
||||
// if (!insertSuccess) {
|
||||
// promptAction.showToast({ message: '下载初始化失败', duration: 2000 });
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// promptAction.showToast({ message: '开始下载,请勿重复点击', duration: 2000 });
|
||||
//
|
||||
// try {
|
||||
// // 配置下载选项
|
||||
// let options: http.HttpRequestOptions = {
|
||||
// method: http.RequestMethod.GET,
|
||||
// header: {
|
||||
// 'Content-Type': 'application/octet-stream'
|
||||
// },
|
||||
// expectDataType: http.HttpDataType.ARRAY_BUFFER,
|
||||
// usingCache: false,
|
||||
// };
|
||||
//
|
||||
// // 执行下载请求
|
||||
// let response = await this.httpRequest.request(downloadUrl, options);
|
||||
// if (response.responseCode === 200) {
|
||||
// // 保存文件到本地
|
||||
// let file = fs.openSync(wpsFile.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
|
||||
// fs.writeSync(file.fd, response.result as ArrayBuffer);
|
||||
// fs.closeSync(file);
|
||||
//
|
||||
// // 验证MD5
|
||||
// let localMd5 = await calculateFileMD5(wpsFile.filePath)
|
||||
// if (localMd5 === fileMd5) {
|
||||
// // 更新数据库状态为下载成功
|
||||
// await this.dbManager.updateDownloadStatus(wpsUuid, 'downloaded');
|
||||
// promptAction.showToast({ message: '下载完成', duration: 2000 });
|
||||
// return true;
|
||||
// } else {
|
||||
// // MD5不匹配,删除文件
|
||||
// fs.unlinkSync(wpsFile.filePath);
|
||||
// await this.dbManager.deleteFile(wpsUuid);
|
||||
// promptAction.showToast({ message: '下载失败,文件校验错误', duration: 2000 });
|
||||
// return false;
|
||||
// }
|
||||
// } else {
|
||||
// // 下载失败
|
||||
// await this.dbManager.deleteFile(wpsUuid);
|
||||
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
|
||||
// return false;
|
||||
// }
|
||||
// } catch (err) {
|
||||
// // 异常处理
|
||||
// await this.dbManager.deleteFile(wpsUuid);
|
||||
// console.error(`下载失败: ${err.code}, ${err.message}`);
|
||||
// promptAction.showToast({ message: '下载失败,请重试', duration: 2000 });
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { deviceInfo } from '@kit.BasicServicesKit';
|
||||
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
|
||||
import { rcp } from '@kit.RemoteCommunicationKit';
|
||||
import { BasicConstant } from '../../../../Index';
|
||||
import { connection } from '@kit.NetworkKit';
|
||||
|
||||
interface HdRequestOptions {
|
||||
baseURL?: string
|
||||
@@ -26,8 +27,14 @@ export interface HdResponse<T> {
|
||||
}
|
||||
export interface TimestampBean {
|
||||
timestamp:string
|
||||
}
|
||||
|
||||
|
||||
export enum NetworkStatus {
|
||||
type_default = 0,//无网络
|
||||
type_wifi = 1,//wifi
|
||||
type_traffic = 2,//蜂窝网络类型(2G/3G/4G/5G)
|
||||
type_unknown = 3,//未知网络
|
||||
type_error = -1,//失败
|
||||
}
|
||||
|
||||
class HdHttp {
|
||||
@@ -431,6 +438,30 @@ class HdHttp {
|
||||
return result === 0 ? (a < b ? -1 : 1) : result;
|
||||
});
|
||||
}
|
||||
|
||||
getNetworkType():number {
|
||||
try {
|
||||
let netHandle = connection.getDefaultNetSync()
|
||||
if (!netHandle || netHandle.netId === 0) {
|
||||
console.info('当前网络状态:无网络')
|
||||
return NetworkStatus.type_default;
|
||||
}
|
||||
let netCap = connection.getNetCapabilitiesSync(netHandle);
|
||||
if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
|
||||
console.info('当前网络状态:wifi')
|
||||
return NetworkStatus.type_wifi;
|
||||
} else if (netCap.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
|
||||
console.info('当前网络状态:2G/3G/4G/5G')
|
||||
return NetworkStatus.type_traffic;
|
||||
} else {
|
||||
console.info('当前网络状态:未知网络')
|
||||
return NetworkStatus.type_unknown;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Get net type failed, error: " + JSON.stringify(error));
|
||||
return NetworkStatus.type_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface DataBlob {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Reference in New Issue
Block a user