From 9a2f82c33c9e91b58a883f0256aaae4fe8bc32ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Thu, 31 Oct 2024 12:23:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=20=E6=B7=BB=E5=8A=A0=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=B4=A9=E6=BA=83=E6=81=A2=E5=A4=8D=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E7=BD=91=E7=BB=9C=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MyAbilityStage 类,实现应用崩溃恢复功能 - 在 ApiElem接口中添加 showLog 字段,用于控制请求日志输出 - 重构 HttpHelper 类,提取公共方法到 HttpHelperX 类 - 优化网络请求方法,增加日志输出和错误处理 - 新增 Base64Helper 和 CharHelper 工具类 --- Index.ets | 7 + .../http/{HttpHelper.ts => HttpHelper.ets} | 189 ++-- src/main/ets/http/HttpHelperX.ts | 73 ++ src/main/ets/utils/Base64Helper.ets | 71 ++ src/main/ets/utils/CharHelper.ets | 105 +++ src/main/ets/utils/CrashHelper.ets | 170 ++++ src/main/ets/utils/FileHelper.ets | 812 ++++++++++++++++++ src/main/ets/utils/LogHelper.ets | 134 +++ src/main/ets/utils/PickerHelper.ets | 219 +++++ src/main/ets/utils/StrHelper.ets | 347 ++++++++ src/main/ets/utils/TimeHelper.ts | 11 +- src/main/ets/utils/ToolsHelper.ets | 5 +- src/main/ets/utils/ToolsHelperForTS.ts | 12 +- 13 files changed, 2024 insertions(+), 131 deletions(-) rename src/main/ets/http/{HttpHelper.ts => HttpHelper.ets} (60%) create mode 100644 src/main/ets/http/HttpHelperX.ts create mode 100644 src/main/ets/utils/Base64Helper.ets create mode 100644 src/main/ets/utils/CharHelper.ets create mode 100644 src/main/ets/utils/CrashHelper.ets create mode 100644 src/main/ets/utils/FileHelper.ets create mode 100644 src/main/ets/utils/LogHelper.ets create mode 100644 src/main/ets/utils/PickerHelper.ets create mode 100644 src/main/ets/utils/StrHelper.ets diff --git a/Index.ets b/Index.ets index 13c096e..c9b075d 100644 --- a/Index.ets +++ b/Index.ets @@ -4,6 +4,13 @@ export { ToolsHelper } from './src/main/ets/utils/ToolsHelper' export { ToolsHelperForTS } from './src/main/ets/utils/ToolsHelperForTS' export { AlgorithmHelper } from './src/main/ets/utils/AlgorithmHelper' +export { Base64Helper } from './src/main/ets/utils/Base64Helper' +export { CharHelper } from './src/main/ets/utils/CharHelper' +export { CrashHelper } from './src/main/ets/utils/CrashHelper' +export { FileHelper } from './src/main/ets/utils/FileHelper' +export { PickerHelper } from './src/main/ets/utils/PickerHelper' +export { StrHelper } from './src/main/ets/utils/StrHelper' +export { LogHelper } from './src/main/ets/utils/LogHelper' /** * 存储相关 diff --git a/src/main/ets/http/HttpHelper.ts b/src/main/ets/http/HttpHelper.ets similarity index 60% rename from src/main/ets/http/HttpHelper.ts rename to src/main/ets/http/HttpHelper.ets index 24d23a0..ab55c94 100644 --- a/src/main/ets/http/HttpHelper.ts +++ b/src/main/ets/http/HttpHelper.ets @@ -1,28 +1,11 @@ import { ArrayList, HashMap } from '@kit.ArkTS'; import http from '@ohos.net.http'; +import { LogHelper } from '../../../../Index'; import { SZYXLocalStorageHelper } from '../utils/SZYXLocalStorageHelper'; import { SZYXLocalStorageKeys } from '../utils/SZYXLocalStorageKeys'; -import { ToolsHelperForTS } from '../utils/ToolsHelperForTS'; +import { HttpHelperX, HttpParamsForm, HttpParamsGet, HttpParamsPost } from './HttpHelperX'; -type HttpParamsGet = { - url: string - query?: Record | Object - headers?: Record -} -type HttpParamsPost = { - url: string - data?: string | Object | ArrayBuffer - query?: Record | Object - headers?: Record -} -type HttpParamsForm = { - url: string - data?: Record | Object|undefined - query?: Record | Object - headers?: Record -} - export class HttpHelper { private static instance: HttpHelper | null = null @@ -51,7 +34,10 @@ export class HttpHelper { * 添加并发白名单 * @param apiNo */ - public addConcurrent(apiNo: string) { + public addConcurrent(apiNo?: string) { + if (!apiNo) { + return + } if (this.concurrentList.getIndexOf(apiNo) === -1) { this.concurrentList.add(apiNo) } @@ -70,7 +56,7 @@ export class HttpHelper { * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 * @returns */ - public postJson(params: HttpParamsPost, apiNo?: string): Promise { + public postJson(params: HttpParamsPost, apiNo?: string, showLog?: boolean): Promise { return new Promise((resolve, reject) => { @@ -79,26 +65,25 @@ export class HttpHelper { this.httpHandlerList.get(apiNo ?? params.url).destroy() this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } let httpRequest = http.createHttp(); if (this.concurrentList.getIndexOf(apiNo ?? params.url) === -1) { this.httpHandlerList.set(apiNo ?? params.url, httpRequest) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } - const header = { - "Content-Type": "application/json;charset=UTF-8", - // "Accept": "application/json", - ...params.headers - } - console.log('=====>', 'POST:', JSON.stringify(params)) - // console.log('=====>', '接口请求', JSON.stringify(header)) - // console.log('=====>', '接口请求', data) + const header = HttpHelperX.getHeaders("application/json;charset=UTF-8", params.headers) - httpRequest.request(this.getUrl(params.url, params.query), { + if (showLog) { + LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params)) + } + + httpRequest.request(HttpHelperX.getUrl(params.url, params.query), { method: http.RequestMethod.POST, connectTimeout: 20000, readTimeout: 20000, @@ -106,18 +91,16 @@ export class HttpHelper { extraData: params.data }) .then((data: http.HttpResponse) => { - console.info(`=====>Result:${data.result as string}(${apiNo})`); - // console.info('=====>' + 'code:' + data.responseCode); - // console.info('=====>' + 'type:' + JSON.stringify(data.resultType)); - // console.info('=====>' + 'header:' + JSON.stringify(data.header)); - // console.info('=====>' + 'cookies:' + data.cookies); // 自API version 8开始支持cookie - // console.info('=====>' + 'header.content-Type:' + JSON.stringify(data.header)); - // console.info('=====>' + 'header.Status-Line:' + JSON.stringify(data.header)); + if (showLog) { + LogHelper.debug(`${apiNo}:\n ${data.result as string}`) + LogHelper.print(data) + } if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (data.responseCode === 200) { resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) @@ -128,11 +111,12 @@ export class HttpHelper { reject(err) } }).catch((err: Error) => { - console.info('=====>' + 'Error:' + JSON.stringify({ err: err, url: params.url, })); + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (err.message === 'Failed writing received data to disk/application') { reject('cancel') @@ -151,7 +135,7 @@ export class HttpHelper { * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 * @returns */ - public postForm(params: HttpParamsForm, apiNo?: string): Promise { + public postForm(params: HttpParamsForm, apiNo?: string, showLog?: boolean): Promise { return new Promise((resolve, reject) => { @@ -160,47 +144,43 @@ export class HttpHelper { this.httpHandlerList.get(apiNo ?? params.url).destroy() this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } let httpRequest = http.createHttp(); if (this.concurrentList.getIndexOf(apiNo ?? params.url) === -1) { this.httpHandlerList.set(apiNo ?? params.url, httpRequest) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } - const header = { - "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", - // "Accept": "application/json", - ...params.headers + const header = HttpHelperX.getHeaders("application/x-www-form-urlencoded;charset=UTF-8", params.headers) + let data = HttpHelperX.getContent(params.data) + + if (showLog) { + LogHelper.debug(`postForm:${apiNo}\n`, JSON.stringify(params)) } - let data = this.getContent(params.data) - console.log('=====>', 'POSTForm:', params.url) - console.log('=====>', 'POSTForm:', JSON.stringify(header)) - console.log('=====>', 'POSTForm:', data) - - httpRequest.request(this.getUrl(params.url, params.query), { + httpRequest.request(HttpHelperX.getUrl(params.url, params.query), { method: http.RequestMethod.POST, connectTimeout: 20000, readTimeout: 20000, header: header, - extraData: data?encodeURI(data):undefined + extraData: data ? encodeURI(data) : undefined }) .then((data: http.HttpResponse) => { - console.info(`=====>Result:${data.result as string}(${apiNo})`); - // console.info('=====>' + 'code:' + data.responseCode); - // console.info('=====>' + 'type:' + JSON.stringify(data.resultType)); - // console.info('=====>' + 'header:' + JSON.stringify(data.header)); - // console.info('=====>' + 'cookies:' + data.cookies); // 自API version 8开始支持cookie - // console.info('=====>' + 'header.content-Type:' + JSON.stringify(data.header)); - // console.info('=====>' + 'header.Status-Line:' + JSON.stringify(data.header)); + if (showLog) { + LogHelper.debug(`${apiNo}:\n ${data.result as string}`) + LogHelper.print(data) + } if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (data.responseCode === 200) { resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) @@ -211,11 +191,12 @@ export class HttpHelper { reject(err) } }).catch((err: Error) => { - console.info('=====>' + 'Error:' + JSON.stringify({ err: err, url: params.url, })); + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (err.message === 'Failed writing received data to disk/application') { reject('cancel') @@ -236,7 +217,7 @@ export class HttpHelper { * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 * @returns */ - public get(params: HttpParamsGet, apiNo?: string): Promise { + public get(params: HttpParamsGet, apiNo?: string, showLog?: boolean): Promise { return new Promise((resolve, reject) => { @@ -245,42 +226,41 @@ export class HttpHelper { this.httpHandlerList.get(apiNo ?? params.url).destroy() this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } let httpRequest = http.createHttp(); if (this.concurrentList.getIndexOf(apiNo ?? params.url) === -1) { this.httpHandlerList.set(apiNo ?? params.url, httpRequest) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } - const header = { - ...params.headers + if (showLog) { + LogHelper.debug(`GET:${apiNo}\n`, HttpHelperX.getUrl(params.url, params.query) + '\n', + JSON.stringify(params.headers)) } - console.log('=====>', 'GET:', this.getUrl(params.url, params.query)) - console.log('=====>', 'header:', JSON.stringify(header)) - httpRequest.request(this.getUrl(params.url, params.query), { + httpRequest.request(HttpHelperX.getUrl(params.url, params.query), { method: http.RequestMethod.GET, connectTimeout: 20000, readTimeout: 20000, - header: header, + header: params.headers, // extraData: params.data }) .then((data: http.HttpResponse) => { - console.info(`=====>Result:${data.result as string}(${apiNo})`); - // console.info('=====>' + 'code:' + data.responseCode); - // console.info('=====>' + 'type:' + JSON.stringify(data.resultType)); - // console.info('=====>' + 'header:' + JSON.stringify(data.header)); - // console.info('=====>' + 'cookies:' + data.cookies); // 自API version 8开始支持cookie - // console.info('=====>' + 'header.content-Type:' + JSON.stringify(data.header)); - // console.info('=====>' + 'header.Status-Line:' + JSON.stringify(data.header)); + if (showLog) { + LogHelper.debug(`${apiNo}:\n${data.result as string}`) + LogHelper.print(data) + } if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (data.responseCode === 200) { resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) @@ -291,11 +271,12 @@ export class HttpHelper { reject(err) } }).catch((err: Error) => { - console.info('=====>' + 'Error:' + JSON.stringify({ err: err, url: params.url, })); + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { this.httpHandlerList.remove(apiNo ?? params.url) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length) } if (err.message === 'Failed writing received data to disk/application') { reject('cancel') @@ -306,42 +287,4 @@ export class HttpHelper { }); } - - private getUrl(url: string, query?: Record | Object) { - let u = url - if (query) { - let q = query - if (typeof query === 'object') { - q = ToolsHelperForTS.classToRecord(query) - } - u = `${u}${u.indexOf('?') < 0 ? '?' : u.endsWith('$') ? '' : '&'}` - Object.entries(q).forEach((row) => { - if (row[1]) { - u = `${u}${row[0]}=${row[1] as string}&` - } - }); - u = u.slice(0, u.length - 1) - } - return u - } - - private getContent(data?: Record | Object) { - if (!data) { - return undefined - } - let u = '' - let q = data - if (typeof data === 'object') { - q = ToolsHelperForTS.classToRecord(data) - } - Object.entries(q).forEach((row) => { - if (row[1]) { - u = `${u}${row[0]}=${row[1] as string}&` - } - }); - u = u.slice(0, u.length - 1) - return u - } - - } \ No newline at end of file diff --git a/src/main/ets/http/HttpHelperX.ts b/src/main/ets/http/HttpHelperX.ts new file mode 100644 index 0000000..ee7a016 --- /dev/null +++ b/src/main/ets/http/HttpHelperX.ts @@ -0,0 +1,73 @@ +import { ToolsHelperForTS } from '../utils/ToolsHelperForTS'; + +export interface HttpParamsGet { + url: string + query?: Record | Object + headers?: Record +} + +export interface HttpParamsPost { + url: string + data?: string | Object | ArrayBuffer + query?: Record | Object + headers?: Record +} + +export interface HttpParamsForm { + url: string + data?: Record | Object | undefined + query?: Record | Object + headers?: Record +} + +export class HttpHelperX { + /** + * + * @param ct Content-Type + * @param headers + * @returns + */ + static getHeaders(ct: string, headers?: Record) { + return { + "Content-Type": ct, + // "Accept": "application/json", + ...headers + } + } + + static getUrl(url: string, query?: Record | Object) { + let u = url + if (query) { + let q = query + if (typeof query === 'object') { + q = ToolsHelperForTS.classToRecord(query) + } + u = `${u}${u.indexOf('?') < 0 ? '?' : u.endsWith('$') ? '' : '&'}` + Object.entries(q).forEach((row) => { + if (row[1]) { + u = `${u}${row[0]}=${row[1] as string}&` + } + }); + u = u.slice(0, u.length - 1) + } + return u + } + + static getContent(data?: Record | Object) { + if (!data) { + return undefined + } + let u = '' + let q = data + if (typeof data === 'object') { + q = ToolsHelperForTS.classToRecord(data) + } + Object.entries(q).forEach((row) => { + if (row[1]) { + u = `${u}${row[0]}=${row[1] as string}&` + } + }); + u = u.slice(0, u.length - 1) + return u + } +} \ No newline at end of file diff --git a/src/main/ets/utils/Base64Helper.ets b/src/main/ets/utils/Base64Helper.ets new file mode 100644 index 0000000..e4f82ee --- /dev/null +++ b/src/main/ets/utils/Base64Helper.ets @@ -0,0 +1,71 @@ +import { util } from '@kit.ArkTS'; + +export class Base64Helper { + private constructor() { + } + + + /** + * 编码,通过输入参数编码后输出Uint8Array对象。 + * @param array + * @returns + */ + static encode(array: Uint8Array): Promise { + let base64 = new util.Base64Helper(); + return base64.encode(array); + } + + /** + * 编码,通过输入参数编码后输出Uint8Array对象。 + * @param array + * @returns + */ + static encodeSync(array: Uint8Array): Uint8Array { + let base64 = new util.Base64Helper(); + let result = base64.encodeSync(array); + return result; + } + + /** + * 编码,通过输入参数编码后输出对应文本。 + * @param array + * @returns + */ + static encodeToStr(array: Uint8Array, options?: util.Type): Promise { + let base64 = new util.Base64Helper(); + return base64.encodeToString(array, options); + } + + /** + * 编码,通过输入参数编码后输出对应文本。 + * @param array + * @returns + */ + static encodeToStrSync(array: Uint8Array, options?: util.Type): string { + let base64 = new util.Base64Helper(); + let result = base64.encodeToStringSync(array, options); + return result; + } + + + /** + * 解码,通过输入参数解码后输出对应Uint8Array对象。 + * @param array + * @returns + */ + static decode(array: Uint8Array | string, options?: util.Type): Promise { + let base64 = new util.Base64Helper(); + return base64.decode(array, options); + } + + /** + * 解码,通过输入参数解码后输出对应Uint8Array对象。 + * @param array + * @returns + */ + static decodeSync(array: Uint8Array | string, options?: util.Type): Uint8Array { + let base64 = new util.Base64Helper(); + let result = base64.decodeSync(array, options); + return result; + } +} \ No newline at end of file diff --git a/src/main/ets/utils/CharHelper.ets b/src/main/ets/utils/CharHelper.ets new file mode 100644 index 0000000..8eb9b02 --- /dev/null +++ b/src/main/ets/utils/CharHelper.ets @@ -0,0 +1,105 @@ +import { i18n } from '@kit.LocalizationKit'; + +export class CharHelper { + private constructor() { + } + + + /** + * 判断字符串char是否是数字 + * @param char + * @returns + */ + static isDigit(char: string): boolean { + return i18n.Unicode.isDigit(char); + } + + /** + * 判断字符串char是否是字母 + * @param char + * @returns + */ + static isLetter(char: string): boolean { + return i18n.Unicode.isLetter(char); + } + + /** + * 判断字符串char是否是小写字母 + * @param char + * @returns + */ + static isLowerCase(char: string): boolean { + return i18n.Unicode.isLowerCase(char); + } + + /** + * 判断字符串char是否是大写字母 + * @param char + * @returns + */ + static isUpperCase(char: string): boolean { + return i18n.Unicode.isUpperCase(char); + } + + /** + * 判断字符串char是否是空格符 + * @param char + * @returns + */ + static isSpaceChar(char: string): boolean { + return i18n.Unicode.isSpaceChar(char); + } + + /** + * 判断字符串char是否是空白符 + * @param char + * @returns + */ + static isWhitespace(char: string): boolean { + return i18n.Unicode.isWhitespace(char); + } + + /** + * 判断字符串char是否是从右到左语言的字符 + * @param char + * @returns + */ + static isRTL(char: string): boolean { + return i18n.Unicode.isRTL(char); + } + + /** + * 判断字符串char是否是表意文字 + * @param char + * @returns + */ + static isIdeograph(char: string): boolean { + return i18n.Unicode.isIdeograph(char); + } + + + /** + * 判断是否空白符 空白符包括空格、制表符、全角空格和不间断空格 + * @param c + * @returns + */ + static isBlankChar(c: number): boolean { + return CharHelper.isWhitespace(c.toString()) + || CharHelper.isSpaceChar(c.toString()) + || c == 0xFEFF || c == 0x202A || c == 0x0000; + } + + + /** + * 判断字符是否位于ASCII范围内(其中0-31是控制字符,32-127表示从A到Z的字母字符) + * @param char 字符 + * @returns + */ + static isAscii(char: string): boolean { + if (char.length == 1) { //确保输入的是单个字符 + return char.charCodeAt(0) < 128; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/ets/utils/CrashHelper.ets b/src/main/ets/utils/CrashHelper.ets new file mode 100644 index 0000000..24d20ef --- /dev/null +++ b/src/main/ets/utils/CrashHelper.ets @@ -0,0 +1,170 @@ +import { appRecovery, common, errorManager, Want } from '@kit.AbilityKit'; +import { TimeHelper } from './TimeHelper'; +import { FileHelper } from './FileHelper'; +import { PreferencesHelper } from './PreferencesHelper'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { StrHelper } from './StrHelper'; +import { ToolsHelper } from './ToolsHelper'; +import { PickerHelper } from './PickerHelper'; +import { LogHelper } from './LogHelper'; + +export class CrashHelper { + private constructor() { + } + + private static readonly idKey: string = 'crash_observer_id_key' + private static observerId: number = -100; + private static ErrorFilePath: string = ''; //错误日志文件路径 + + + /** + * 注册错误观测器。注册后可以捕获到应用产生的js crash,应用崩溃时进程不会退出。将异常信息写入本地文件。 + */ + static onError() { + try { + PreferencesHelper.get(CrashHelper.idKey).then(res => { + if (res !== undefined && (res as number) !== -100) { + CrashHelper.offError(); //如果存在,就先注销错误观测器。 + } + CrashHelper.observerId = errorManager.on('error', { + onUnhandledException(errMsg) { + let errStr = `${TimeHelper.getTime()} - 异常信息:\n${errMsg}\n\n\n`; + LogHelper.error(errMsg) + CrashHelper.ErrorFilePath = FileHelper.getFilesDirPath("ErrorLog", "errorLog.txt") + FileHelper.writeEasy(CrashHelper.ErrorFilePath, errStr); + }, + onException(errObject) { + // let errStr = `${DateUtil.getTodayStr()} - 异常信息2:\n${JSON.stringify(errObject)}\n\n\n`; + } + }); + PreferencesHelper.put(CrashHelper.idKey, CrashHelper.observerId) + }) + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `CrashHelper-onError-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + } + + + /** + * 注销错误观测器。 + */ + static offError() { + try { + if (CrashHelper.observerId === -100) { + return + } + errorManager.off('error', CrashHelper.observerId, (err: BusinessError) => { + if (err) { + hilog.error(0x0000, '=====>', "CrashHelper:" + JSON.stringify(err)) + return; + } + PreferencesHelper.put(CrashHelper.idKey, -100) + }) + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `CrashHelper-offError-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + } + + + /** + * 导出错误日志 + */ + static onExportErrorLog() { + CrashHelper.ErrorFilePath = FileHelper.getFilesDirPath("ErrorLog", "errorLog.txt") + if (StrHelper.isNotEmpty(CrashHelper.ErrorFilePath)) { + PickerHelper.saveDocument(['errorLog.txt']).then((documentSaveResult: Array) => { + if (documentSaveResult && documentSaveResult.length > 0) { + let saveUri = documentSaveResult[0]; + let file = FileHelper.openSync(saveUri); + FileHelper.copyFile(CrashHelper.ErrorFilePath, file.fd).then(() => { + FileHelper.close(file.fd); + }); + } + }) + } else { + ToolsHelper.showMessage('暂无日志文件') + } + } + + /** + * 清空已有日志 + */ + static clearErrorLog() { + FileHelper.unlinkSync(CrashHelper.ErrorFilePath) + CrashHelper.ErrorFilePath = FileHelper.getFilesDirPath("ErrorLog", "errorLog.txt") + } + + + /** + * 读取错误日志文件 + */ + static async readErrorText(): Promise { + CrashHelper.ErrorFilePath = FileHelper.getFilesDirPath("ErrorLog", "errorLog.txt") + if (StrHelper.isNotEmpty(CrashHelper.ErrorFilePath)) { + if (FileHelper.accessSync(CrashHelper.ErrorFilePath)) { + return await FileHelper.readText(CrashHelper.ErrorFilePath); + } + } + return ''; + } + + + /** + * 启用应用恢复功能,参数按顺序填入。该接口调用后,应用从启动器启动时第一个Ability支持恢复。 + * @param restart RestartFlag 应用重启标志。 + * ALWAYS_RESTART 0 总是重启应用。 + * RESTART_WHEN_JS_CRASH 0x0001 发生JS_CRASH时重启应用。 + * RESTART_WHEN_APP_FREEZE 0x0002 发生APP_FREEZE时重启应用。 + * NO_RESTART 0xFFFF 总是不重启应用。 + * @param saveOccasion SaveOccasionFlag 保存条件标志 + * SAVE_WHEN_ERROR 0x0001 当发生应用故障时保存。 + * SAVE_WHEN_BACKGROUND 0x0002 当应用切入后台时保存。 + * @param saveMode SaveModeFlag 状态保存标志 + * SAVE_WITH_FILE 0x0001 每次状态保存都会写入到本地文件缓存。 + * SAVE_WITH_SHARED_MEMORY 0x0002 状态先保存在内存中,应用故障退出时写入到本地文件缓存。 + */ + static enableAppRecovery(restart: appRecovery.RestartFlag = appRecovery.RestartFlag.ALWAYS_RESTART, + saveOccasion: appRecovery.SaveOccasionFlag = appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR, + saveMode: appRecovery.SaveModeFlag.SAVE_WITH_FILE = appRecovery.SaveModeFlag.SAVE_WITH_FILE) { + appRecovery.enableAppRecovery(restart, saveOccasion, saveMode); + } + + + /** + * 重启APP,并拉起应用启动时第一个Ability,可以配合errorManager相关接口使用。 + * 如果该Ability存在已经保存的状态,这些状态数据会在Ability的OnCreate生命周期回调的want参数中作为wantParam属性传入。 + * API10时将启动由setRestartWant指定的Ability。如果没有指定则按以下规则启动: + * 如果当前应用前台的Ability支持恢复,则重新拉起该Ability。 + * 如果存在多个支持恢复的Ability处于前台,则只拉起最后一个。 + * 如果没有Ability处于前台,则不拉起。 + */ + static restartApp() { + appRecovery.restartApp() + } + + + /** + * 设置下次恢复主动拉起场景下的Ability。该Ability必须为当前包下的UIAbility。 + * @param want 通过设置Want中"bundleName"和"abilityName"字段来指定恢复重启的Ability。 + */ + static setRestartWant(want: Want) { + appRecovery.setRestartWant(want); + } + + + /** + * 保存当前App状态 或 主动保存Ability的状态,这个状态将在下次恢复启动时使用。可以配合errorManager相关接口使用 + * @param context UIAbilityContext 需要保存状态的UIAbility所对应的context。 + * @returns + */ + static saveAppState(context?: common.UIAbilityContext): boolean { + if (context) { + return appRecovery.saveAppState(context) //主动保存Ability的状态 + } else { + return appRecovery.saveAppState() //保存当前App状态 + } + } +} \ No newline at end of file diff --git a/src/main/ets/utils/FileHelper.ets b/src/main/ets/utils/FileHelper.ets new file mode 100644 index 0000000..c2bd0b3 --- /dev/null +++ b/src/main/ets/utils/FileHelper.ets @@ -0,0 +1,812 @@ +import fileUri from '@ohos.file.fileuri'; +import fs, { ListFileOptions, ReadOptions, ReadTextOptions, WriteOptions } from '@ohos.file.fs'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { StrHelper } from './StrHelper'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +export class FileHelper { + private constructor() { + } + + static readonly separator: string = '/'; + + /** + * 获取文件目录下的文件夹路径或文件路径。 + * @param dirPath 文件路径;支持完整路径和相对路径(download/wps/doc);dirPath传空字符串表示根目录 + * @param fileName 文件名(test.text);fileName传空字符串表示文件夹路径 + * @param blHap true:HAP级别文件路径、 false:App级别文件路径 + * @returns + */ + static getFilesDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string { + let filePath = blHap ? getContext().filesDir : getContext().getApplicationContext().filesDir; //根目录 + if (StrHelper.isNotEmpty(dirPath)) { + if (StrHelper.startsWith(dirPath, filePath)) { //路径中包含根目录,是完整路径。 + filePath = dirPath; + } else { //路径中不包含根目录,拼接成完整路径。 + filePath = filePath + FileHelper.separator + dirPath; + } + if (!FileHelper.accessSync(filePath)) { + FileHelper.mkdirSync(filePath) //如果文件夹不存在就创建 + } + } + if (StrHelper.isNotEmpty(fileName)) { + filePath = filePath + FileHelper.separator + fileName; + } + return filePath; + } + + + /** + * 获取缓存目录下的文件夹路径或文件路径。 + * @param dirPath 文件路径;支持完整路径和相对路径(download/wps/doc);dirPath传空字符串表示根目录 + * @param fileName 文件名(test.text);fileName传空字符串表示文件夹路径 + * @param blHap true:HAP级别文件路径、 false:App级别文件路径 + * @returns + */ + static getCacheDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string { + let filePath = blHap ? getContext().cacheDir : getContext().getApplicationContext().cacheDir; //根目录 + if (StrHelper.isNotEmpty(dirPath)) { + if (FileHelper.hasDirPath(dirPath)) { //路径中包含根目录,是完整路径。 + filePath = dirPath; + } else { //路径中不包含根目录,拼接成完整路径。 + filePath = filePath + FileHelper.separator + dirPath; + } + if (!FileHelper.accessSync(filePath)) { + FileHelper.mkdirSync(filePath) //如果文件夹不存在就创建 + } + } + if (StrHelper.isNotEmpty(fileName)) { + filePath = filePath + FileHelper.separator + fileName; + } + return filePath; + } + + + /** + * 获取临时目录下的文件夹路径或文件路径。 + * @param dirPath 文件路径;支持完整路径和相对路径(download/wps/doc);dirPath传空字符串表示根目录 + * @param fileName 文件名(test.text);fileName传空字符串表示文件夹路径 + * @param blHap true:HAP级别文件路径、 false:App级别文件路径 + * @returns + */ + static getTempDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string { + let filePath = blHap ? getContext().tempDir : getContext().getApplicationContext().tempDir; //根目录 + if (StrHelper.isNotEmpty(dirPath)) { + if (FileHelper.hasDirPath(dirPath)) { //路径中包含根目录,是完整路径。 + filePath = dirPath; + } else { //路径中不包含根目录,拼接成完整路径。 + filePath = filePath + FileHelper.separator + dirPath; + } + if (!FileHelper.accessSync(filePath)) { + FileHelper.mkdirSync(filePath) //如果文件夹不存在就创建 + } + } + if (StrHelper.isNotEmpty(fileName)) { + filePath = filePath + FileHelper.separator + fileName; + } + return filePath; + } + + + /** + * 判断是否是完整路径 + * @param path 文件路径 + */ + static hasDirPath(path: string): boolean { + return StrHelper.startsWith(path, "/data/storage/el1/") || StrHelper.startsWith(path, "/data/storage/el2/"); + } + + + /** + * 通过URI或路径,获取FileUri + * @param uriOrPath URI或路径 + * @returns + */ + static getFileUri(uriOrPath: string): fileUri.FileUri { + return new fileUri.FileUri(uriOrPath); + } + + /** + * 通过URI或路径,获取文件名。 + * @param uriOrPath URI或路径 + * @returns + */ + static getFileName(uriOrPath: string): string { + return FileHelper.getFileUri(uriOrPath).name; + } + + /** + * 通过URI或路径,获取文件路径 + * @param uriOrPath URI或路径 + * @returns + */ + static getFilePath(uriOrPath: string): string { + return FileHelper.getFileUri(uriOrPath).path; + } + + + /** + * 通过URI或路径,获取对应文件父目录的URI。 + * @param uriOrPath URI或路径 + */ + static getParentUri(uriOrPath: string): string { + return FileHelper.getFileUri(uriOrPath).getFullDirectoryUri(); + } + + /** + * 通过URI或路径,获取对应文件父目录的路径名。 + * @param uriOrPath URI或路径 + */ + static getParentPath(uriOrPath: string): string { + let parentUri = FileHelper.getParentUri(uriOrPath); + return FileHelper.getFilePath(parentUri) + } + + /** + * 以同步方法获取文件URI。 + * @param path 应用沙箱路径 + * @returns + */ + static getUriFromPath(path: string): string { + return fileUri.getUriFromPath(path); + } + + + /** + * 根据文件名获取文件后缀 + * @param fileName 例如: test.txt test.doc + * @returns + */ + static getFileExtention(fileName: string) { + if (StrHelper.isNotEmpty(fileName) && fileName.includes(".")) { + return fileName.substring(fileName.lastIndexOf(".") + 1); + } + return ''; + } + + + /** + * 获取指定文件夹下所有文件的大小或指定文件大小。 + * @param path 文件夹路径 或 文件路径 + */ + static getFileDirSize(path: string): number { + if (FileHelper.accessSync(path)) { //path存在 + if (FileHelper.isDirectory(path)) { //文件夹 + let count: number = 0; + FileHelper.listFileSync(path, { recursion: true }).forEach((filePath) => { + count = count + FileHelper.lstatSync(path + filePath).size + }) + return count; + } else { //文件 + return FileHelper.lstatSync(path).size + } + } + return 0; + } + + + /** + * 判断文件是否是普通文件。 + * @param file string|number 文件应用沙箱路径path或已打开的文件描述符fd。 + * @returns + */ + static isFile(file: string | number): boolean { + return fs.statSync(file).isFile(); + } + + /** + * 判断文件是否是目录。 + * @param file string|number 文件应用沙箱路径path或已打开的文件描述符fd。 + * @returns + */ + static isDirectory(file: string | number): boolean { + return fs.statSync(file).isDirectory(); + } + + + /** + * 重命名文件或文件夹,使用Promise异步回调。 + * @param oldPath string 文件的应用沙箱原路径。 + * @param newPath string 文件的应用沙箱新路径。 + * @returns + */ + static rename(oldPath: string, newPath: string): Promise { + return fs.rename(oldPath, newPath); + } + + /** + * 重命名文件或文件夹,以同步方法。 + * @param oldPath string 文件的应用沙箱原路径。 + * @param newPath string 文件的应用沙箱新路径。 + * @returns + */ + static renameSync(oldPath: string, newPath: string) { + fs.renameSync(oldPath, newPath); + } + + + /** + * 创建目录,当recursion指定为true,可多层级创建目录,使用Promise异步回调。 + * @param path 目录的应用沙箱路径。 + * @param recursion 是否多层级创建目录。recursion指定为true时,可多层级创建目录。recursion指定为false时,仅可创建单层目录。 + * @returns + */ + static mkdir(path: string, recursion: boolean = true): Promise { + if (recursion) { + return fs.mkdir(path, recursion); + } else { + return fs.mkdir(path); + } + } + + /** + * 创建目录以同步方法,当recursion指定为true,可多层级创建目录。 + * @param path 目录的应用沙箱路径。 + * @param recursion 是否多层级创建目录。recursion指定为true时,可多层级创建目录。recursion指定为false时,仅可创建单层目录。 + */ + static mkdirSync(path: string, recursion: boolean = true) { + if (recursion) { + fs.mkdirSync(path, recursion); + } else { + fs.mkdirSync(path); + } + } + + + /** + * 删除整个目录,使用Promise异步回调。 + * @param path 目录的应用沙箱路径。 + * @returns + */ + static rmdir(path: string): Promise { + return fs.rmdir(path); + } + + /** + * 删除整个目录,以同步方法。 + * @param path 目录的应用沙箱路径。 + */ + static rmdirSync(path: string) { + return fs.rmdirSync(path); + } + + + /** + * 删除单个文件,使用Promise异步回调。 + * @param path 文件的应用沙箱路径。 + * @returns + */ + static unlink(path: string): Promise { + return fs.unlink(path); + } + + /** + * 删除单个文件,以同步方法。 + * @param path 文件的应用沙箱路径。 + * @returns + */ + static unlinkSync(path: string) { + fs.unlinkSync(path); + } + + + /** + * 检查文件是否存在,使用Promise异步回调。 + * @param path 文件应用沙箱路径。 + * @returns + */ + static access(path: string): Promise { + return fs.access(path); + } + + /** + * 检查文件是否存在,以同步方法。 + * @param path 文件应用沙箱路径。 + * @returns + */ + static accessSync(path: string): boolean { + return fs.accessSync(path); + } + + + /** + * 打开文件,支持使用URI打开文件。使用Promise异步回调。 + * @param path string 文件的应用沙箱路径或URI。 + * @param mode number 打开文件的选项,必须指定如下选项中的一个,默认以只读方式打开。 + * @returns + */ + static open(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): Promise { + return fs.open(path, mode); + } + + /** + * 打开文件,支持使用URI打开文件。以同步方法。 + * @param path string 文件的应用沙箱路径或URI。 + * @param mode number 打开文件的选项,必须指定如下选项中的一个,默认以只读方式打开。 + * @returns + */ + static openSync(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): fs.File { + return fs.openSync(path, mode); + } + + + /** + * 从文件读取数据,使用Promise异步回调。 + * @param fd number 已打开的文件描述符。 + * @param buffer ArrayBuffer 用于保存读取到的文件数据的缓冲区。 + * @param options 支持如下选项: + * offset,number类型,表示期望读取文件的位置。可选,默认从当前位置开始读。 + * length,number类型,表示期望读取数据的长度。可选,默认缓冲区长度。 + * @returns + */ + static read(fd: number, buffer: ArrayBuffer, options?: ReadOptions): Promise { + return fs.read(fd, buffer, options) + } + + /** + * 从文件读取数据,以同步方法。 + * @param fd number 已打开的文件描述符。 + * @param buffer ArrayBuffer 用于保存读取到的文件数据的缓冲区。 + * @param options 支持如下选项: + * offset,number类型,表示期望读取文件的位置。可选,默认从当前位置开始读。 + * length,number类型,表示期望读取数据的长度。可选,默认缓冲区长度。 + * @returns + */ + static readSync(fd: number, buffer: ArrayBuffer, options?: ReadOptions): number { + return fs.readSync(fd, buffer, options) + } + + /** + * 基于文本方式读取文件(即直接读取文件的文本内容),使用Promise异步回调。 + * @param filePath 文件的应用沙箱路径。 + * @param options 支持如下选项: + * offset,number类型,表示期望读取文件的位置。可选,默认从当前位置开始读取。 + * length,number类型,表示期望读取数据的长度。可选,默认文件长度。 + * encoding,string类型,当数据是 string 类型时有效,表示数据的编码方式,默认 'utf-8',仅支持 'utf-8'。 + * @returns + */ + static readText(filePath: string, options?: ReadTextOptions): Promise { + return fs.readText(filePath, options); + } + + /** + * 基于文本方式读取文件(即直接读取文件的文本内容),以同步方法。 + * @param filePath 文件的应用沙箱路径。 + * @param options 支持如下选项: + * offset,number类型,表示期望读取文件的位置。可选,默认从当前位置开始读取。 + * length,number类型,表示期望读取数据的长度。可选,默认文件长度。 + * encoding,string类型,当数据是 string 类型时有效,表示数据的编码方式,默认 'utf-8',仅支持 'utf-8'。 + * @returns + */ + static readTextSync(filePath: string, options?: ReadTextOptions): string { + return fs.readTextSync(filePath, options); + } + + + /** + * 将数据写入文件,使用Promise异步回调。 + * @param fd number 已打开的文件描述符。 + * @param buffer ArrayBuffer|string 待写入文件的数据,可来自缓冲区或字符串。 + * @param options 支持如下选项: + * offset,number类型,表示期望写入文件的位置。可选,默认从当前位置开始写。 + * length,number类型,表示期望写入数据的长度。可选,默认缓冲区长度。 + * encoding,string类型,当数据是string类型时有效,表示数据的编码方式,默认 'utf-8'。当前仅支持 'utf-8'。 + * @returns + */ + static write(fd: number, buffer: ArrayBuffer | string, options?: WriteOptions): Promise { + return fs.write(fd, buffer, options) + } + + /** + * 将数据写入文件,以同步方法。 + * @param fd number 已打开的文件描述符。 + * @param buffer ArrayBuffer|string 待写入文件的数据,可来自缓冲区或字符串。 + * @param options 支持如下选项: + * offset,number类型,表示期望写入文件的位置。可选,默认从当前位置开始写。 + * length,number类型,表示期望写入数据的长度。可选,默认缓冲区长度。 + * encoding,string类型,当数据是string类型时有效,表示数据的编码方式,默认 'utf-8'。当前仅支持 'utf-8'。 + * @returns + */ + static writeSync(fd: number, buffer: ArrayBuffer | string, options?: WriteOptions): number { + return fs.writeSync(fd, buffer, options) + } + + /** + * 将数据写入文件,并关闭文件。 + * @param path string 文件的应用沙箱路径或URI。 + * @param buffer ArrayBuffer|string 待写入文件的数据,可来自缓冲区或字符串。 + * @param append 是否追加,true-追加,false-不追加(直接覆盖) + * @returns + */ + static async writeEasy(path: string, buffer: ArrayBuffer | string, append: boolean = true): Promise { + try { + let file = FileHelper.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); + let offset = append ? FileHelper.statSync(file.fd).size : 0 + let options: WriteOptions = { offset: offset, encoding: 'utf-8' }; + return await FileHelper.write(file.fd, buffer, options).finally(() => { + FileHelper.close(file.fd); //关闭文件 + }); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `FileHelper-writeEasy-异常 ~ code: ${error.code} -·- message: ${error.message}`) + return -1; + } + } + + + /** + * 关闭文件,使用Promise异步回调。 + * @param file 已打开的File对象或已打开的文件描述符fd。 + * @returns + */ + static close(file: fs.File | number): Promise { + return fs.close(file); + } + + /** + * 关闭文件,以同步方法。 + * @param file 已打开的File对象或已打开的文件描述符fd。 + */ + static closeSync(file: fs.File | number) { + fs.closeSync(file); + } + + + /** + * 列出文件夹下所有文件名,支持递归列出所有文件名(包含子目录下),支持文件过滤,使用Promise异步回调。 + * @param path string 文件夹的应用沙箱路径。 + * @param options 文件过滤选项。默认不进行过滤。 + * recursion boolean 是否递归子目录下文件名,默认为false。 + * listNum number 列出文件名数量。当设置0时,列出所有文件,默认为0。 + * filter Filter 文件过滤选项。当前仅支持后缀名匹配、文件名模糊查询、文件大小过滤、最近修改时间过滤。 + * @returns + */ + static listFile(path: string, options?: ListFileOptions): Promise { + return fs.listFile(path, options); + } + + /** + * 列出文件夹下所有文件名,支持递归列出所有文件名(包含子目录下),支持文件过滤,以同步方法。 + * @param path string 文件夹的应用沙箱路径。 + * @param options 文件过滤选项。默认不进行过滤。 + * recursion boolean 是否递归子目录下文件名,默认为false。 + * listNum number 列出文件名数量。当设置0时,列出所有文件,默认为0。 + * filter Filter 文件过滤选项。当前仅支持后缀名匹配、文件名模糊查询、文件大小过滤、最近修改时间过滤。 + * @returns + */ + static listFileSync(path: string, options?: ListFileOptions): string[] { + return fs.listFileSync(path, options); + } + + + /** + * 获取文件详细属性信息,使用Promise异步回调。 + * @param file string|number 文件应用沙箱路径path或已打开的文件描述符fd。 + */ + static stat(file: string | number): Promise { + return fs.stat(file); + } + + /** + * 获取文件详细属性信息,以同步方法。 + * @param file string|number 文件应用沙箱路径path或已打开的文件描述符fd。 + * @returns + */ + static statSync(file: string | number): fs.Stat { + return fs.statSync(file); + } + + + /** + * 拷贝文件或者目录,支持拷贝进度监听,使用Promise异步返回。 + * @param srcUri 待复制文件或目录的uri。 + * @param destUri 目标文件或目录的uri。 + * @param options options中提供拷贝进度回调: + * ProgressListener 拷贝进度监听。 + * @returns + */ + static copy(srcUri: string, destUri: string, options?: fs.CopyOptions): Promise { + return fs.copy(srcUri, destUri, options); + } + + /** + * 复制文件,使用Promise异步回调。 + * @param src string|number 待复制文件的路径或待复制文件的文件描述符。 + * @param dest string|number 目标文件路径或目标文件的文件描述符。 + * @param mode number 提供覆盖文件的选项,当前仅支持0,且默认为0。0:完全覆盖目标文件。 + * @returns + */ + static copyFile(src: string | number, dest: string | number, mode: number = 0): Promise { + return fs.copyFile(src, dest, mode); + } + + /** + * 以同步方法复制文件。 + * @param src string|number 待复制文件的路径或待复制文件的文件描述符。 + * @param dest string|number 目标文件路径或目标文件的文件描述符。 + * @param mode number 提供覆盖文件的选项,当前仅支持0,且默认为0。0:完全覆盖目标文件。 + */ + static copyFileSync(src: string | number, dest: string | number, mode: number = 0) { + fs.copyFileSync(src, dest, mode); + } + + /** + * 复制源文件夹至目标路径下,只能复制沙箱里的文件夹,使用Promise异步返回。 + * @param src 源文件夹的应用沙箱路径。 + * @param dest 目标文件夹的应用沙箱路径。 + * @param mode 复制模式: + * mode为0,文件级别抛异常。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则抛出异常。源文件夹下未冲突的文件全部移动至目标文件夹下,目标文件夹下未冲突文件将继续保留,且冲突文件信息将在抛出异常的data属性中以Array形式提供。 + * mode为1,文件级别强制覆盖。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则强制覆盖冲突文件夹下所有同名文件,未冲突文件将继续保留。 + * @returns + */ + static copyDir(src: string, dest: string, mode: number = 1): Promise { + return fs.copyDir(src, dest, mode); + } + + /** + * 以同步方法复制源文件夹至目标路径下,只能复制沙箱里的文件夹。 + * @param src 源文件夹的应用沙箱路径。 + * @param dest 目标文件夹的应用沙箱路径。 + * @param mode 复制模式: + * mode为0,文件级别抛异常。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则抛出异常。源文件夹下未冲突的文件全部移动至目标文件夹下,目标文件夹下未冲突文件将继续保留,且冲突文件信息将在抛出异常的data属性中以Array形式提供。 + * mode为1,文件级别强制覆盖。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则强制覆盖冲突文件夹下所有同名文件,未冲突文件将继续保留。 + * @returns + */ + static copyDirSync(src: string, dest: string, mode: number = 1) { + fs.copyDirSync(src, dest, mode); + } + + + /** + * 移动文件,使用Promise异步回调。 + * @param src string 源文件的应用沙箱路径。 + * @param dest string 目的文件的应用沙箱路径。 + * @param mode number 移动模式。若mode为0,移动位置存在同名文件时,强制移动覆盖。若mode为1,移动位置存在同名文件时,抛出异常。默认为0。 + * @returns + */ + static moveFile(src: string, dest: string, mode: number = 0): Promise { + return fs.moveFile(src, dest, mode) + } + + /** + * 移动文件,以同步方法。 + * @param src string 源文件的应用沙箱路径。 + * @param dest string 目的文件的应用沙箱路径。 + * @param mode number 移动模式。若mode为0,移动位置存在同名文件时,强制移动覆盖。若mode为1,移动位置存在同名文件时,抛出异常。默认为0。 + * @returns + */ + static moveFileSync(src: string, dest: string, mode: number = 0) { + fs.moveFileSync(src, dest, mode) + } + + /** + * 移动源文件夹至目标路径下,使用Promise异步返回。 + * @param src 源文件夹的应用沙箱路径 + * @param dest 目标文件夹的应用沙箱路径 + * @param mode 移动模式: + * mode为0,文件夹级别抛异常。若目标文件夹下存在与源文件夹名冲突的非空文件夹,则抛出异常。 + * mode为1,文件级别抛异常。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则抛出异常。源文件夹下未冲突的文件全部移动至目标文件夹下,目标文件夹下未冲突文件将继续保留,且冲突文件信息将在抛出异常的data属性中以Array形式提供。 + * mode为2,文件级别强制覆盖。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则强制覆盖冲突文件夹下所有同名文件,未冲突文件将继续保留。 + * mode为3,文件夹级别强制覆盖。移动源文件夹至目标文件夹下,目标文件夹下移动的文件夹内容与源文件夹完全一致。若目标文件夹下存在与源文件夹名冲突的文件夹,该文件夹下所有原始文件将不会保留。 + * @returns + */ + static moveDir(src: string, dest: string, mode: number = 3): Promise { + return fs.moveDir(src, dest, mode); + } + + /** + * 以同步方法移动源文件夹至目标路径下。 + * @param src 源文件夹的应用沙箱路径 + * @param dest 目标文件夹的应用沙箱路径 + * @param mode 移动模式: + * mode为0,文件夹级别抛异常。若目标文件夹下存在与源文件夹名冲突的非空文件夹,则抛出异常。 + * mode为1,文件级别抛异常。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则抛出异常。源文件夹下未冲突的文件全部移动至目标文件夹下,目标文件夹下未冲突文件将继续保留,且冲突文件信息将在抛出异常的data属性中以Array形式提供。 + * mode为2,文件级别强制覆盖。目标文件夹下存在与源文件夹名冲突的文件夹,若冲突文件夹下存在同名文件,则强制覆盖冲突文件夹下所有同名文件,未冲突文件将继续保留。 + * mode为3,文件夹级别强制覆盖。移动源文件夹至目标文件夹下,目标文件夹下移动的文件夹内容与源文件夹完全一致。若目标文件夹下存在与源文件夹名冲突的文件夹,该文件夹下所有原始文件将不会保留。 + * @returns + */ + static moveDirSync(src: string, dest: string, mode: number = 3) { + return fs.moveDirSync(src, dest, mode); + } + + + /** + * 截断文件,使用Promise异步回调。 + * @param file string|number 文件的应用沙箱路径或已打开的文件描述符fd。 + * @param len number 文件截断后的长度,以字节为单位。默认为0。 + * @returns + */ + static truncate(file: string | number, len: number = 0): Promise { + return fs.truncate(file, len) + } + + /** + * 截断文件,以同步方法。 + * @param file string|number 文件的应用沙箱路径或已打开的文件描述符fd。 + * @param len number 文件截断后的长度,以字节为单位。默认为0。 + * @returns + */ + static truncateSync(file: string | number, len: number = 0) { + fs.truncateSync(file, len) + } + + + /** + * 获取链接文件信息,使用Promise异步回调。 + * @param path string 文件的应用沙箱路径。 + * @returns + */ + static lstat(path: string): Promise { + return fs.lstat(path); + } + + /** + * 获取链接文件信息,以同步方法。 + * @param path string 文件的应用沙箱路径。 + * @returns + */ + static lstatSync(path: string): fs.Stat { + return fs.lstatSync(path); + } + + + /** + * 同步文件数据,使用Promise异步回调。 + * @param fd number 已打开的文件描述符。 + * @returns + */ + static fsync(fd: number): Promise { + return fs.fsync(fd); + } + + /** + * 同步文件数据,以同步方法。 + * @param fd number 已打开的文件描述符。 + */ + static fsyncSync(fd: number) { + fs.fsyncSync(fd); + } + + + /** + * 实现文件内容数据同步,使用Promise异步回调。 + * @param fd number 已打开的文件描述符。 + * @returns + */ + static fdatasync(fd: number): Promise { + return fs.fdatasync(fd); + } + + /** + * 实现文件内容数据同步,以同步方法。 + * @param fd number 已打开的文件描述符。 + */ + static fdatasyncSync(fd: number) { + fs.fdatasyncSync(fd); + } + + + /** + * 基于文件路径打开文件流,使用Promise异步回调。 + * @param path string 文件的应用沙箱路径。 + * @param mode string 文件打开类型 + * r:打开只读文件,该文件必须存在。 + * r+:打开可读写的文件,该文件必须存在。 + * w:打开只写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * w+:打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。 + * a+:以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 + * @returns + */ + static createStream(path: string, mode: string = 'r'): Promise { + return fs.createStream(path, mode) + } + + /** + * 基于文件路径打开文件流,以同步方法。 + * @param path string 文件的应用沙箱路径。 + * @param mode string 文件打开类型 + * r:打开只读文件,该文件必须存在。 + * r+:打开可读写的文件,该文件必须存在。 + * w:打开只写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * w+:打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。 + * a+:以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 + * @returns + */ + static createStreamSync(path: string, mode: string = 'r'): fs.Stream { + return fs.createStreamSync(path, mode); + } + + /** + * 基于文件描述符打开文件流,使用Promise异步回调。 + * @param fd number 已打开的文件描述符。 + * @param mode string 文件打开类型 + * r:打开只读文件,该文件必须存在。 + * r+:打开可读写的文件,该文件必须存在。 + * w:打开只写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * w+:打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。 + * a+:以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 + * @returns + */ + static fdopenStream(fd: number, mode: string = 'r'): Promise { + return fs.fdopenStream(fd, mode) + } + + /** + * 基于文件描述符打开文件流,以同步方法。 + * @param fd number 已打开的文件描述符。 + * @param mode string 文件打开类型 + * r:打开只读文件,该文件必须存在。 + * r+:打开可读写的文件,该文件必须存在。 + * w:打开只写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * w+:打开可读写文件,若文件存在则文件长度清0,即该文件内容会消失。若文件不存在则建立该文件。 + * a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。 + * a+:以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 + * @returns + */ + static fdopenStreamSync(fd: number, mode: string = 'r'): fs.Stream { + return fs.fdopenStreamSync(fd, mode) + } + + + /** + * 创建临时目录,使用Promise异步回调。 + * @param prefix string 用随机产生的字符串替换以“XXXXXX”结尾目录路径。 + * @returns + */ + static mkdtemp(prefix: string): Promise { + return fs.mkdtemp(prefix); + } + + /** + * 创建临时目录,以同步的方法。 + * @param prefix string 用随机产生的字符串替换以“XXXXXX”结尾目录路径。 + * @returns + */ + static mkdtempSync(prefix: string): string { + return fs.mkdtempSync(prefix); + } + + + /** + * 将文件描述符转化为File。 + * @param fd 文件描述符。 + * @returns + */ + static dup(fd: number): fs.File { + return fs.dup(fd); + } + + + /** + * 修改文件最近访问时间属性。 + * path 文件的应用沙箱路径。 + * mtime 待更新的时间戳。自1970年1月1日起至目标时间的毫秒数。仅支持修改文件最近访问时间属性。 + * @returns + */ + static utimes(path: string, mtime: number): void { + fs.utimes(path, mtime); + } + + + /** + * 格式化文件大小 + * @param fileSize + * @returns + */ + static getFormatFileSize(fileSize: number): string { + if (fileSize < 1024) { + return fileSize + "B"; + } else if (fileSize < 1024 * 1024) { + return (fileSize / 1024).toFixed(1) + "KB"; + } else if (fileSize < 1024 * 1024 * 1024) { + return (fileSize / (1024 * 1024)).toFixed(1) + "MB"; + } else if (fileSize < 1024 * 1024 * 1024 * 1024) { + return (fileSize / (1024 * 1024 * 1024)).toFixed(1) + "GB"; + } else { + return (fileSize / (1024 * 1024 * 1024 * 1024)).toFixed(1) + "TB"; + } + } +} \ No newline at end of file diff --git a/src/main/ets/utils/LogHelper.ets b/src/main/ets/utils/LogHelper.ets new file mode 100644 index 0000000..3ec442d --- /dev/null +++ b/src/main/ets/utils/LogHelper.ets @@ -0,0 +1,134 @@ +import hilog from '@ohos.hilog' +import { BusinessError } from '@kit.BasicServicesKit' + +const LOGGER_DOMAIN: number = 0x0000 +const LOGGER_TAG: string = '=====》' + +export class LogHelper { + private static domain: number = LOGGER_DOMAIN + private static tag: string = LOGGER_TAG //日志Tag + private static format: string = '%{public}s' + private static showLog: boolean = true //是否显示打印日志 + + + /** + * 初始化日志参数(该方法建议在Ability里调用) + * @param domain + * @param tag + * @param showLog + */ + static init(domain: number = LOGGER_DOMAIN, tag: string = LOGGER_TAG, showLog: boolean = true) { + LogHelper.domain = domain + LogHelper.tag = tag + LogHelper.showLog = showLog + } + + /** + * 设置日志对应的领域标识,范围是0x0~0xFFFF。(该方法建议在Ability里调用) + * @param domain + */ + static setDomain(domain: number = LOGGER_DOMAIN) { + LogHelper.domain = domain + } + + /** + * 设置日志标识(该方法建议在Ability里调用) + * @param tag + */ + static setTag(tag: string = LOGGER_TAG) { + LogHelper.tag = tag + } + + /** + * 是否打印日志(该方法建议在Ability里调用) + * @param showLog + */ + static setShowLog(showLog: boolean = true) { + LogHelper.showLog = showLog + } + + /** + * 打印DEBUG级别日志 + * @param args + */ + static debug(...args: string[]): void { + if (LogHelper.showLog) { + hilog.debug(LogHelper.domain, LogHelper.tag, ' ') + hilog.debug(LogHelper.domain, LogHelper.tag, LogHelper.format.repeat(args.length), args) + } + } + + /** + * 打印INFO级别日志 + * @param args + */ + static info(...args: string[]): void { + if (LogHelper.showLog) { + hilog.info(LogHelper.domain, LogHelper.tag, ' ') + hilog.info(LogHelper.domain, LogHelper.tag, LogHelper.format.repeat(args.length), args) + } + } + + /** + * 打印WARN级别日志 + * @param args + */ + static warn(...args: string[]): void { + if (LogHelper.showLog) { + hilog.warn(LogHelper.domain, LogHelper.tag, ' ') + hilog.warn(LogHelper.domain, LogHelper.tag, LogHelper.format.repeat(args.length), args) + } + } + + /** + * 打印ERROR级别日志 + * @param args + */ + static error(...args: string[]): void { + if (LogHelper.showLog) { + hilog.error(LogHelper.domain, LogHelper.tag, ' ') + hilog.error(LogHelper.domain, LogHelper.tag, LogHelper.format.repeat(args.length), args) + } + } + + /** + * 打印FATAL级别日志 + * @param args + */ + static fatal(...args: string[]): void { + if (LogHelper.showLog) { + hilog.fatal(LogHelper.domain, LogHelper.tag, ' ') + hilog.fatal(LogHelper.domain, LogHelper.tag, LogHelper.format.repeat(args.length), args) + } + } + + + /** + * 打印JSON对象和JSON字符串 + * @param obj + */ + static print(obj: object | string) { + try { + console.debug('') + if (typeof obj === 'object') { + let str = JSON.stringify(obj, null, 2) + let arr: string[] = str.split('\n') + for (let index = 0; index < arr.length; index++) { + console.debug(arr[index]) + } + } else { + obj = JSON.parse(obj) + let str = JSON.stringify(obj, null, 2) + let arr = str.split('\n') + for (let index = 0; index < arr.length; index++) { + console.debug(arr[index]) + } + } + } catch (err) { + let error = err as BusinessError; //异常了,说明不是JSON字符串 + LogHelper.error(`LogHelper-print-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + } +} + +// export default new LogHelper() //单例(在ES6模块中,当你使用 import 导入一个模块时,实际上是在导入该模块的值的一个引用。这意味着在另一个模块中修改该值会影响原始模块中的值。) \ No newline at end of file diff --git a/src/main/ets/utils/PickerHelper.ets b/src/main/ets/utils/PickerHelper.ets new file mode 100644 index 0000000..6d0fa6a --- /dev/null +++ b/src/main/ets/utils/PickerHelper.ets @@ -0,0 +1,219 @@ +import picker from '@ohos.file.picker'; +import { camera, cameraPicker } from '@kit.CameraKit'; +import { common } from '@kit.AbilityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + + +const DOCUMENT_DEFAULT_SELECT_NUMBER: number = 9; //数量 + +export class PickerHelper { + private constructor() { + } + + + /** + * 调用系统相机,拍照、录视频 + * @param options + * @returns + */ + static async camera(options?: CameraOptions): Promise { + try { + if (!options) { + options = new CameraOptions(); + } + if (!options.mediaTypes || options.mediaTypes.length == 0) { + options.mediaTypes = [cameraPicker.PickerMediaType.PHOTO, cameraPicker.PickerMediaType.VIDEO]; + } + let pickerProfile: cameraPicker.PickerProfile = { + cameraPosition: options.cameraPosition ? options.cameraPosition : camera.CameraPosition.CAMERA_POSITION_BACK, + videoDuration: options.videoDuration, + saveUri: options.saveUri + }; + + let context = getContext() as common.Context; + let pickerResult: cameraPicker.PickerResult = await cameraPicker.pick(context, + options.mediaTypes, pickerProfile); + if (pickerResult && pickerResult.resultUri) { + return pickerResult.resultUri; + } + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `PickerHelper-camera-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + return ""; + } + + + /** + * 通过选择模式拉起PhotoViewPicker界面,用户可以选择一个或多个图片/视频。 + * @param options + * @returns + */ + static async selectPhoto(options?: picker.PhotoSelectOptions): Promise> { + try { + if (!options) { + options = new picker.PhotoSelectOptions(); + } + if (!options.MIMEType) { //可选择的媒体文件类型,若无此参数,则默认为图片和视频类型。 + options.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; + } + if (!options.maxSelectNumber) { //选择媒体文件数量的最大值,默认9 + options.maxSelectNumber = DOCUMENT_DEFAULT_SELECT_NUMBER; + } + let photoPicker = new picker.PhotoViewPicker(); + let photoSelectResult: picker.PhotoSelectResult = await photoPicker.select(options); + if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) { + return photoSelectResult.photoUris; + } else { + return []; + } + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', + `PickerHelper-selectPhoto-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } + + + /** + * 通过保存模式拉起photoPicker进行保存图片或视频资源的文件名,若无此参数,则默认需要用户自行输入 + * @param newFileNames + */ + static async savePhoto(newFileNames?: Array): Promise> { + try { + let photoPicker = new picker.PhotoViewPicker(); + if (newFileNames == undefined || newFileNames == null || newFileNames.length == 0) { + let photoSaveResult = await photoPicker.save(); + if (photoSaveResult && photoSaveResult.length > 0) { + return photoSaveResult; + } else { + return []; + } + } else { + let PhotoSaveOptions = new picker.PhotoSaveOptions(); + PhotoSaveOptions.newFileNames = newFileNames; + let photoSaveResult = await photoPicker.save(PhotoSaveOptions); + if (photoSaveResult && photoSaveResult.length > 0) { + return photoSaveResult; + } else { + return []; + } + } + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `PickerHelper-savePhoto-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } + + + /** + * 通过选择模式拉起documentPicker界面,用户可以选择一个或多个文件。 + * @param options + * @returns + */ + static async selectDocument(options?: picker.DocumentSelectOptions): Promise> { + try { + if (!options) { + options = new picker.DocumentSelectOptions(); + } + if (!options.maxSelectNumber) { //选择媒体文件数量的最大值,默认9 + options.maxSelectNumber = DOCUMENT_DEFAULT_SELECT_NUMBER; + } + if (!options.selectMode) { //支持选择的资源类型,默认文件 + options.selectMode = picker.DocumentSelectMode.FILE; + } + let documentPicker = new picker.DocumentViewPicker(); + return await documentPicker.select(options); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', + `PickerHelper-selectDocument-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } + + + /** + * 通过保存模式拉起documentPicker界面,用户可以保存一个或多个文件。 + * @param options + * @returns + */ + static async saveDocument(newFileNames?: Array): Promise> { + try { + let documentPicker = new picker.DocumentViewPicker(); + if (newFileNames == undefined || newFileNames == null || newFileNames.length == 0) { + return await documentPicker.save(); + } else { + let documentSaveOptions = new picker.DocumentSaveOptions(); + documentSaveOptions.newFileNames = newFileNames; + return await documentPicker.save(documentSaveOptions); + } + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', + `PickerHelper-saveDocument-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } + + + /** + * 通过选择模式拉起audioPicker界面(目前拉起的是documentPicker,audioPicker在规划中),用户可以选择一个或多个音频文件。 + * @returns + */ + static async selectAudio(options?: picker.AudioSelectOptions): Promise> { + try { + if (!options) { + options = new picker.AudioSelectOptions(); + } + if (!options.maxSelectNumber) { //选择媒体文件数量的最大值,默认9 + options.maxSelectNumber = DOCUMENT_DEFAULT_SELECT_NUMBER; + } + let audioPicker = new picker.AudioViewPicker(); + return await audioPicker.select(options); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', + `PickerHelper-selectAudio-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } + + + /** + * 通过保存模式拉起audioPicker界面(目前拉起的是documentPicker,audioPicker在规划中),用户可以保存一个或多个音频文件。 + * @param newFileNames + * @returns + */ + static async saveAudio(newFileNames?: Array): Promise> { + try { + let audioPicker = new picker.AudioViewPicker(); + if (newFileNames == undefined || newFileNames == null || newFileNames.length == 0) { + return await audioPicker.save(); + } else { + let AudioSaveOptions = new picker.AudioSaveOptions(); + AudioSaveOptions.newFileNames = newFileNames; + return await audioPicker.save(AudioSaveOptions); + } + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, '=====>', `PickerHelper-saveAudio-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return []; + } + } +} + + +/** + * 相机参数类 + */ +export class CameraOptions { + mediaTypes: Array = + [cameraPicker.PickerMediaType.PHOTO, cameraPicker.PickerMediaType.VIDEO]; //媒体类型。 + cameraPosition: camera.CameraPosition = camera.CameraPosition.CAMERA_POSITION_BACK; //相机的位置。 + saveUri?: string; //保存配置信息的uri。 + videoDuration?: number; //录制的最大时长。 +} \ No newline at end of file diff --git a/src/main/ets/utils/StrHelper.ets b/src/main/ets/utils/StrHelper.ets new file mode 100644 index 0000000..c599ce3 --- /dev/null +++ b/src/main/ets/utils/StrHelper.ets @@ -0,0 +1,347 @@ +import { CharHelper } from './CharHelper'; +import { buffer, util } from '@kit.ArkTS'; +import { Base64Helper } from './Base64Helper'; + +export class StrHelper { + private constructor() { + } + + + /** + * 判断字符串是否为空(undefined、null) + * @param str 被检测的字符串 + * @returns 是否为空 + */ + static isNull(str: string | undefined | null): boolean { + return str == undefined || str == null; + } + + /** + * 判断字符串是否为非空。true为非空空,否则false + * @param str + * @returns + */ + static isNotNull(str: string | undefined | null): boolean { + return false == StrHelper.isNull(str); + } + + + /** + * 判断字符串是否为空(undefined、null、字符串长度为0) + * @param str 被检测的字符串 + * @return 是否为空 + */ + static isEmpty(str: string | undefined | null): boolean { + return str == undefined || str == null || str.length == 0; + } + + /** + * 判断字符串是否为非空。true为非空空,否则false + * @param str + * @returns + */ + static isNotEmpty(str: string | undefined | null): boolean { + return false == StrHelper.isEmpty(str); + } + + + /** + * 判断字符串是否为空和空白符(空白符包括空格、制表符、全角空格和不间断空格)。true为空,否则false + * @param str + * @returns + */ + static isBlank(str: string | undefined | null): boolean { + let length: number = 0; + if ((str == undefined) || (str == null) || ((length = str.length) == 0)) { + return true; + } + for (let i = 0; i < length; i++) { + if (false == CharHelper.isBlankChar(str.charCodeAt(i))) { + return false; //只要有一个非空字符即为非空字符串 + } + } + return true; + } + + /** + * 判断字符串是否为非空和空白符(空白符包括空格、制表符、全角空格和不间断空格)true为非空,否则false + * @param str + * @returns + */ + static isNotBlank(str: string | undefined | null): boolean { + return false == StrHelper.isBlank(str); + } + + + /** + * 格式化字符串 + * @param source + * @param defaultValue + * @returns + */ + static toStr(source: string | null | undefined, defaultValue = ""): string { + if (source == null || source == undefined) { + return defaultValue; + } + return String(source); + } + + + /** + * 替换字符串中匹配的正则为给定的字符串 + * @param str 待替换的字符串 + * @param pattern 要匹配的内容正则或字符串 + * @param replaceValue 替换的内容 + * @returns + */ + static replace(str: string, pattern: RegExp | string, replaceValue: string = ''): string { + return str.replace(pattern, replaceValue); + } + + /** + * 替换字符串中所有匹配的正则为给定的字符串 + * @param str 待替换的字符串 + * @param pattern 要匹配的内容正则或字符串 + * @param replaceValue 替换的内容 + * @returns 返回替换后的字符串 + */ + static replaceAll(str: string, pattern: RegExp | string, replaceValue: string = ''): string { + return str.replaceAll(pattern, replaceValue); + } + + /** + * 判断字符串是否以给定的字符串开头 + * @param string 要检索的字符串 + * @param target 要检索字符 + * @param position 检索的位置 + * @returns + */ + static startsWith(string: string = '', target: string, position: number = 0): boolean { + return string.startsWith(target, position); + } + + + /** + * 判断字符串是否以给定的字符串结尾 + * @param str 要检索的字符串 + * @param target 要检索字符 + * @param position 检索的位置 + * @returns + */ + static endsWith(str: string = '', target: string, position: number = str.length): boolean { + return str.endsWith(target, position); + } + + + /** + * 将字符串重复指定次数 + * @param str 要重复的字符串 + * @param n 重复的次数 + * @returns + */ + static repeat(str: string = '', n: number = 1): string { + return str.repeat(n); + } + + + /** + * 将整个字符串转换为小写 + * @param str 要转换的字符串 + * @returns 返回小写的字符串 + */ + static toLower(str: string = ''): string { + return str.toLowerCase(); + } + + + /** + * 将整个字符串转换为大写 + * @param str 要转换的字符串 + * @returns 返回小写的字符串 + */ + static toUpper(str: string = ''): string { + return str.toUpperCase(); + } + + + /** + * 将字符串首字母转换为大写,剩下为小写 + * @param str 待转换的字符串 + * @returns 转换后的 + */ + static capitalize(str: string = ''): string { + if (StrHelper.isNotEmpty(str)) { + const firstChar = str.charAt(0).toUpperCase(); + const restChars = str.slice(1).toLowerCase(); + return firstChar + restChars; + } + return ''; + } + + + /** + * 判断两个传入的数值或者是字符串是否相等 + * @param source + * @param target + * @returns + */ + static equal(source: string | number, target: string | number): boolean { + return source === target; + } + + /** + * 判断两个传入的数值或者是字符串是否不相等 + * @param source + * @param target + * @returns + */ + static notEqual(source: string | number, target: string | number): boolean { + return false == StrHelper.equal(source, target); + } + + + /** + * 字符串转Uint8Array + * @param src 字符串 + * @returns Uint8Array + */ + public static strToUint8Array(src: string, encoding: buffer.BufferEncoding = 'utf-8'): Uint8Array { + let textEncoder = new util.TextEncoder(encoding); + let result = textEncoder.encodeInto(src); + return result; + } + + /** + * Uint8Array转字符串 + * @param src Uint8Array + * @returns 字符串 + */ + static unit8ArrayToStr(src: Uint8Array, encoding: buffer.BufferEncoding = 'utf-8'): string { + let textDecoder = util.TextDecoder.create(encoding, { ignoreBOM: true }) + let result = textDecoder.decodeWithStream(src, { stream: true }); + return result; + } + + + /** + * 16进制字符串转换unit8Array + * @param hexStr + * @returns + */ + static strToHex(hexStr: string): Uint8Array { + return new Uint8Array(buffer.from(hexStr, 'hex').buffer); + } + + /** + * 16进制unit8Array转字符串 + * @param arr + * @returns + */ + static hexToStr(arr: Uint8Array): string { + return buffer.from(arr).toString('hex'); + } + + + /** + * Bytes转字符串 + * @param bytes + * @returns + */ + public static bytesToStr(bytes: Uint8Array): string { + let str = "" + for (let i = 0; i < bytes.length; i++) { + str += String.fromCharCode(bytes[i]); + + } + return str; + } + + /** + * 字符串转Bytes + * @param str + * @returns + */ + public static strToBytes(str: string): Uint8Array { + let bytes: number[] = new Array(); + for (let i = 0; i < str.length; i++) { + bytes.push(str.charCodeAt(i)) + } + return new Uint8Array(bytes); + } + + + /** + * 字符串转Base64字符串 + * @param src 字符串 + * @returns + */ + static strToBase64(src: string): string { + let uint8Array = StrHelper.strToUint8Array(src); + let result = Base64Helper.encodeToStrSync(uint8Array); + return result; + } + + + /** + * Base64字符串转字符串 + * @param base64Str Base64字符串 + * @returns + */ + static base64ToStr(base64Str: string): string { + let uint8Array = Base64Helper.decodeSync(base64Str); + let result = StrHelper.unit8ArrayToStr(uint8Array); + return result; + } + + + /** + * 字符串转ArrayBuffer + * @param str + * @returns + */ + static strToBuffer(src: string, encoding: buffer.BufferEncoding = 'utf-8'): ArrayBuffer { + let buf = buffer.from(src, encoding); + return buf.buffer; + } + + /** + * ArrayBuffer转字符串 + * @param str + * @returns + */ + static bufferToStr(src: ArrayBuffer, encoding: buffer.BufferEncoding = 'utf-8'): string { + let buf = buffer.from(src); + let result = buf.toString(encoding); + return result; + } + + + /** + * ArrayBuffer转Uint8Array + * @param str + * @returns + */ + static bufferToUint8Array(src: ArrayBuffer): Uint8Array { + return new Uint8Array(src); + } + + /** + * Uint8Array转ArrayBuffer + * @param str + * @returns + */ + static unit8ArrayToBuffer(src: Uint8Array): ArrayBuffer { + // return buffer.from(src).buffer; + return src.buffer as ArrayBuffer; + } + + + /** + * 获取系统错误码对应的详细信息 + * @param errno 错误码 + * @returns + */ + static getErrnoToString(errno: number): string { + return util.errnoToString(errno); + } +} \ No newline at end of file diff --git a/src/main/ets/utils/TimeHelper.ts b/src/main/ets/utils/TimeHelper.ts index e0d7373..73d0683 100644 --- a/src/main/ets/utils/TimeHelper.ts +++ b/src/main/ets/utils/TimeHelper.ts @@ -25,17 +25,20 @@ export class TimeHelper { /** * 获取当前时间,指定返回样式 * @param formats 时间格式 - * 'yyyy年MM月dd日' + * 'yyyy-MM-dd HH:mm:ss' */ + static getTime(format: string = 'yyyy-MM-dd HH:mm:ss') { + return TimeHelper.formatDate(new Date(),format) + } static formatDate(date: Date, format: string) { const replacements: { [key: string]: string } = { - YYYY: date.getFullYear().toString(), + yyyy: date.getFullYear().toString(), MM: String(date.getMonth() + 1).padStart(2, "0"), - DD: String(date.getDate()).padStart(2, "0"), + dd: String(date.getDate()).padStart(2, "0"), HH: String(date.getHours()).padStart(2, "0"), mm: String(date.getMinutes()).padStart(2, "0"), ss: String(date.getSeconds()).padStart(2, "0") }; - return format.replace(/YYYY|MM|DD|HH|mm|ss/g, matched => replacements[matched]); + return format.replace(/yyyy|MM|dd|HH|mm|ss/g, matched => replacements[matched]); } } diff --git a/src/main/ets/utils/ToolsHelper.ets b/src/main/ets/utils/ToolsHelper.ets index a6c0444..93fa3a0 100644 --- a/src/main/ets/utils/ToolsHelper.ets +++ b/src/main/ets/utils/ToolsHelper.ets @@ -4,6 +4,7 @@ import { HashMap } from '@kit.ArkTS'; import { DeviceInfo } from '../bean/DeviceInfo'; import { common } from '@kit.AbilityKit'; import { md5_hex } from '../util/md5'; +import { LogHelper } from '../../../../Index'; export interface Btn { text?: string | Resource; @@ -105,7 +106,7 @@ export class ToolsHelper { */ static log(...args: ESObject[]) { const k = ToolsHelper.getStackKey()?.split('/') - console.log(`========>${k ? k[k.length-1].split('.')[0] : ''}::`, args, '\n') + LogHelper.info(`${k ? k[k.length-1].split('.')[0] : ''}::\n`, ...args) } /** @@ -349,7 +350,7 @@ export class ToolsHelper { } private static getUniqueId(fun: Function): string { - ToolsHelper.log(ToolsHelper.getStackKey()) + // ToolsHelper.log(ToolsHelper.getStackKey()) if (!ToolsHelper.uniqueIdMap.has(fun)) { ToolsHelper.uniqueIdMap.set(fun, ToolsHelper.getUuid()); } diff --git a/src/main/ets/utils/ToolsHelperForTS.ts b/src/main/ets/utils/ToolsHelperForTS.ts index da0da04..2695886 100644 --- a/src/main/ets/utils/ToolsHelperForTS.ts +++ b/src/main/ets/utils/ToolsHelperForTS.ts @@ -1,4 +1,4 @@ -import { HashMap } from "@kit.ArkTS"; +import { HashMap } from '@kit.ArkTS'; /** * 常用方法,部分方法,在ets里面不能用 @@ -18,7 +18,7 @@ export class ToolsHelperForTS { return myMap; } - public static classToRecord(obj: Object): Record { + public static classToRecord(obj: Object): Record { const record: Record = {} as Record; for (const key in obj) { if (obj.hasOwnProperty(key)) { @@ -27,4 +27,12 @@ export class ToolsHelperForTS { } return record; } + + static gets(headers?: Record) { + return { + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + // "Accept": "application/json", + ...headers + } + } } \ No newline at end of file