中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

做外貿(mào)沒(méi)網(wǎng)站可以嗎小說(shuō)推廣平臺(tái)有哪些

做外貿(mào)沒(méi)網(wǎng)站可以嗎,小說(shuō)推廣平臺(tái)有哪些,網(wǎng)站顯示速度的代碼是什么情況,深圳網(wǎng)站優(yōu)化多少錢(qián)一、案例實(shí)現(xiàn) (一) 案例效果截圖 (二) 案例運(yùn)用到的知識(shí)點(diǎn) 核心知識(shí)點(diǎn) 文件管理能力 :該模塊為基礎(chǔ)文件操作API,提供基礎(chǔ)文件操作能力,包括文件基本管理、文件目錄管理、文件信息統(tǒng)計(jì)、文件流式讀寫(xiě)等常用功能。(ohos.file.f…

一、案例實(shí)現(xiàn)

(一) 案例效果截圖
?

(二) 案例運(yùn)用到的知識(shí)點(diǎn)

  1. 核心知識(shí)點(diǎn)
  • 文件管理能力 :該模塊為基礎(chǔ)文件操作API,提供基礎(chǔ)文件操作能力,包括文件基本管理、文件目錄管理、文件信息統(tǒng)計(jì)、文件流式讀寫(xiě)等常用功能。(@ohos.file.fs)
  • 選擇器 : 選擇器(Picker)模塊封裝了PhotoViewPicker、DocumentViewPicker、AudioViewPicker等API模塊,提供文件選擇與保存能力。(@ohos.file.picker)
  • 相冊(cè)管理模塊 :該模塊提供相冊(cè)管理模塊能力,包括創(chuàng)建相冊(cè)以及訪問(wèn)、修改相冊(cè)中的媒體數(shù)據(jù)信息等。(@ohos.file.photoAccessHelper)
  • PhotoViewPicker :圖庫(kù)選擇器對(duì)象,用來(lái)支撐選擇圖片/視頻和保存圖片/視頻等用戶場(chǎng)景。
  • DocumentViewPicker :文件選擇器對(duì)象,用來(lái)支撐選擇和保存各種格式文檔。
  1. 其他知識(shí)點(diǎn)
  • ArkTS 語(yǔ)言基礎(chǔ)
  • V2版狀態(tài)管理:@ComponentV2/@Local
  • 自定義組件
  • 自定義構(gòu)建函數(shù)@Builder
  • 內(nèi)置組件:SaveButton/Tabs/Scroll/Column/Text/TextArea/Button
  • 常量與資源分類的訪問(wèn)
  • MVVM模式

(三) 代碼結(jié)構(gòu)

├──entry/src/main/ets                      // 代碼區(qū)
│  ├──common
│  │  └──utils
│  │     ├──Logger.ets                     // 日志打印類
│  │     ├──PictureSaving.ets              // 圖片保存方法
│  │     ├──ReadFile.ets                   // 文件讀取方法
│  │     ├──SavingAndSelectUserFile.ets    // 用戶文件保存與選擇方法
│  │     └──WriteFile.ets                  // 文件寫(xiě)入方法
│  ├──entryability
│  │  └──EntryAbility.ets                  // 程序入口類
│  ├──pages
│  │  └──HomePage.ets                      // 主界面  
│  └──view
│     ├──ApplicationFileTab.ets            // 應(yīng)用文件功能展示
│     └──PublicFilesTab.ets                // 公共文件功能展示
└──entry/src/main/resources                // 資源文件目錄

(四) 公共文件與資源

本案例涉及到的常量類和工具類代碼如下:

  1. 通用日志類
// entry/src/main/ets/common/utils/Logger.ets
import { hilog } from '@kit.PerformanceAnalysisKit'const LOGGER_PREFIX: string = 'File_Management'
class Logger {private domain: numberprivate prefix: stringprivate format: string = '%{public}s, %{public}s'constructor(prefix: string = '', domain: number = 0xFF00) {this.prefix = prefixthis.domain = domain}debug(...args: string[]): void {hilog.debug(this.domain, this.prefix, this.format, args)}info(...args: string[]): void {hilog.info(this.domain, this.prefix, this.format, args)}warn(...args: string[]): void {hilog.warn(this.domain, this.prefix, this.format, args)}error(...args: string[]): void {hilog.error(this.domain, this.prefix, this.format, args)}
}export default new Logger(LOGGER_PREFIX, 0xFF02)

本案例涉及到的資源文件如下:

  1. string.json
// entry/src/main/resources/base/element/string.json
{"string": [{"name": "module_desc","value": "模塊描述"},{"name": "EntryAbility_desc","value": "description"},{"name": "EntryAbility_label","value": "文件管理"},{"name": "textarea_default","value": "請(qǐng)輸入保存至文件的內(nèi)容"},{"name": "file_content","value": "文件內(nèi)容"},{"name": "button1","value": "保存至應(yīng)用沙箱目錄"},{"name": "button2","value": "讀取保存的文件內(nèi)容"},{"name": "title","value": "文件管理"},{"name": "bar1","value": "應(yīng)用文件"},{"name": "bar2","value": "公共文件"},{"name": "select_photo","value": "從圖庫(kù)中選擇一張圖片展示"},{"name": "textarea_default2","value": "文件內(nèi)容..."},{"name": "button3","value": "保存test.txt至用戶目錄"},{"name": "button4","value": "讀取test.txt文件內(nèi)容"}]
}
  1. color.json
// entry/src/main/resources/base/element/color.json
{"color": [{"name": "start_window_background","value": "#FFFFFF"},{"name": "text_color","value": "#182431"},{"name": "picture_background","value": "#0D182431"}]
}
  1. float.json
// entry/src/main/resources/base/element/float.json
{"float": [{"name": "default_22","value": "22vp"},{"name": "default_24","value": "24vp"},{"name": "default_294","value": "294vp"},{"name": "default_312","value": "312vp"},{"name": "default_40","value": "40vp"},{"name": "default_16","value": "16vp"},{"name": "default_13","value": "13vp"},{"name": "default_336","value": "336vp"},{"name": "default_8","value": "8vp"},{"name": "default_139","value": "139vp"},{"name": "default_20","value": "20vp"},{"name": "default_100","value": "100vp"},{"name": "default_48","value": "48vp"},{"name": "default_147","value": "147vp"},{"name": "default_10","value": "10vp"},{"name": "default_223","value": "223vp"},{"name": "default_302","value": "302vp"},{"name": "default_213","value": "213vp"},{"name": "default_120","value": "120vp"},{"name": "default_251","value": "251vp"},{"name": "default_41","value": "41vp"},{"name": "default_30","value": "30vp"},{"name": "default_7","value": "7vp"},{"name": "default_360","value": "360vp"},{"name": "default_56","value": "56vp"},{"name": "default_680","value": "680vp"},{"name": "default_200","value": "200vp"},{"name": "default_12","value": "12vp"}]
}

其他資源請(qǐng)到源碼中獲取。

(五) 沙箱路徑下的文件讀寫(xiě)

  1. 構(gòu)建主界面
// entry/src/main/ets/pages/HomePage.ets
import { ApplicationFileTab } from '../views/ApplicationFileTab'@Entry
@ComponentV2
struct HomePage {build() {Column() {Column() {Text($r('app.string.title')).width($r('app.float.default_312')).height($r('app.float.default_41')).textAlign(TextAlign.Start).fontSize($r('app.float.default_30')).fontFamily('HarmonyHeiTi-Bold').fontColor($r('app.color.text_color')).lineHeight($r('app.float.default_41')).fontWeight(700).margin({top: $r('app.float.default_7'),bottom: $r('app.float.default_8'),left: $r('app.float.default_24'),right: $r('app.float.default_24')})}.width($r('app.float.default_360')).height($r('app.float.default_56'))Tabs() {TabContent() {ApplicationFileTab()}.tabBar(new SubTabBarStyle($r('app.string.bar1')).indicator({ marginTop: $r('app.float.default_8') }).labelStyle({ font: { size: $r('app.float.default_16') } }))TabContent() {Scroll() {Text('PublicFilesTab')}.height($r('app.float.default_680'))}.height('100%').tabBar(new SubTabBarStyle($r('app.string.bar2')).indicator({marginTop : $r('app.float.default_8')}).labelStyle({font : {size : $r('app.float.default_16')}}))}.barWidth($r('app.float.default_200')).barHeight($r('app.float.default_56')).width('100%')}.backgroundColor($r('app.color.picture_background')).justifyContent(FlexAlign.Center).width('100%')}
}
  1. 應(yīng)用文件組件
// entry/src/main/ets/views/ApplicationFileTab.ets
import { readFile } from '../common/utils/ReadFile'
import { writeFile } from '../common/utils/WriteFile'@ComponentV2
export struct ApplicationFileTab {// 用于記錄讀取的內(nèi)容。@Local message: string = ''// 用于記錄文本框中的內(nèi)容。@Local content: string = ''build() {Column() {Text($r('app.string.textarea_default')).width($r('app.float.default_294')).height($r('app.float.default_22')).fontColor($r('app.color.text_color')).fontWeight(500).fontSize($r('app.float.default_16')).fontFamily('HarmonyHeiTi-Medium').lineHeight($r('app.float.default_22')).textAlign(TextAlign.Start).margin({top: $r('app.float.default_13'),bottom: $r('app.float.default_13'),right: $r('app.float.default_8')})TextArea({ text: this.content }).width($r('app.float.default_336')).height($r('app.float.default_139')).borderRadius($r('app.float.default_24')).backgroundColor($r('app.color.start_window_background')).enableKeyboardOnFocus(false).onChange((value: string) => {this.content = value})Text($r('app.string.file_content')).width($r('app.float.default_294')).height($r('app.float.default_22')).fontSize($r('app.float.default_16')).lineHeight($r('app.float.default_22')).fontWeight(500).margin({top: $r('app.float.default_13'),bottom: $r('app.float.default_13'),right: $r('app.float.default_8')})TextArea({ text: this.message }).enableKeyboardOnFocus(false).width($r('app.float.default_336')).height($r('app.float.default_139')).backgroundColor($r('app.color.start_window_background')).borderRadius($r('app.float.default_24'))Column() {Button($r('app.string.button1')).width($r('app.float.default_312')).height($r('app.float.default_40'))// 寫(xiě)入信息并清空輸入內(nèi)容,點(diǎn)擊“保存”后清空文本框。.onClick(() => {writeFile(this.content)this.content = ''})Button($r('app.string.button2')).width($r('app.float.default_312')).height($r('app.float.default_40')).margin({top: $r('app.float.default_12'),bottom: $r('app.float.default_100')}).onClick(() => {this.message = readFile()})}.width('100%').margin({ top: $r('app.float.default_100') })}.width('100%').height('100%')}
}
  1. 實(shí)現(xiàn)沙箱路徑下文件的寫(xiě)入操作
// entry/src/main/ets/common/utils/WriteFile.ets
import { fileIo } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDir/*** writeFile.* 將內(nèi)容寫(xiě)入文件* @param content 要寫(xiě)入文件的內(nèi)容*/
export function writeFile(content: string): void {let filePath = filesDir + '/test.txt'// 基于文件路徑打開(kāi)文件流let fileStream = fileIo.createStreamSync(filePath, "w+")fileStream.writeSync(content)fileStream.close()
}

關(guān)鍵代碼說(shuō)明:

  • 首先需要結(jié)合context得到想要操作的文件路徑。
  • 然后對(duì)該文件進(jìn)行寫(xiě)入操作,這里使用的是createStreamSync創(chuàng)建文件流的形式。
  • 通過(guò)文件流的方式可以控制文件的讀寫(xiě)方式,覆蓋寫(xiě)的方式可以使得每次寫(xiě)操作互不影響。
  1. 實(shí)現(xiàn)沙箱路徑下文件的讀取操作
// entry/src/main/ets/common/utils/ReadFile.ets
import { fileIo } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
import { buffer } from '@kit.ArkTS'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDir
let res: string = ''/*** readFile.* 讀取文件內(nèi)容并返回字符串。* @return 字符串。*/
export function readFile(): string {let filePath = filesDir + '/test.txt'let stat = fileIo.statSync(filePath)let size = stat.sizelet buf = new ArrayBuffer(size)// 基于文件路徑打開(kāi)文件流let fileStream = fileIo.createStreamSync(filePath, "r+")// 讀取文件流信息fileStream.readSync(buf)// 將讀取的信息轉(zhuǎn)換為字符串類型并返回let con = buffer.from(buf, 0)res = con.toString()fileStream.close()return res
}

關(guān)鍵代碼說(shuō)明:

  • 與寫(xiě)入類似,也通過(guò)文件路徑去創(chuàng)建文件流,進(jìn)行讀取操作。
  • 通過(guò)statSync獲取文件的詳細(xì)信息,進(jìn)而得到文件內(nèi)容的大小,然后利用文件流將文件內(nèi)容讀取到ArrayBuffer中,再將ArrayBuffer轉(zhuǎn)化為string返回,即可完成文件的讀取操作。

(六) 圖庫(kù)的讀取與寫(xiě)入

  1. 在主界面引入公共文件組件
// entry/src/main/ets/pages/HomePage.ets
// ...
import { publicFilesTab } from '../views/PublicFilesTab'@Entry
@ComponentV2
struct HomePage {build() {Column() {// ...Tabs() {// ...TabContent() {Scroll() {publicFilesTab()}// ...}// ...}// ...}// ...}
}
  1. 構(gòu)建公共文件組件(實(shí)現(xiàn)保存圖片到圖庫(kù))
// entry/src/main/ets/views/PublicFilesTab.ets
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { fileIo } from '@kit.CoreFileKit'
import Logger from '../common/utils/Logger'
import { photoPickerGetUri } from '../common/utils/PictureSaving'@ComponentV2
export struct publicFilesTab {@Local picture: string = ''@Local flag: Boolean = false@Local message: string = ''@Local content: string = ''@Local isInput: Boolean = false// 設(shè)置安全控件的按鈕屬性@Local saveButtonOptions: SaveButtonOptions = {icon: SaveIconStyle.FULL_FILLED,text: SaveDescription.SAVE_IMAGE,buttonType: ButtonType.Capsule}build() {Column() {Column() {Image($r('app.media.img')).borderRadius($r('app.float.default_24')).width($r('app.float.default_312')).height($r('app.float.default_147'))// 創(chuàng)建安全控件按鈕SaveButton(this.saveButtonOptions).onClick(async (event, result: SaveButtonOnClickResult) => {if (result == SaveButtonOnClickResult.SUCCESS) {try {Logger.info('createAsset 成功, event: ' + event)let context = getContext()let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)// 創(chuàng)建媒體文件let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg')Logger.info('createAsset 成功, uri: ' + uri)// 打開(kāi)創(chuàng)建的媒體文件,讀取本地文件并轉(zhuǎn)換為 ArrayBuffer,便于寫(xiě)入let file = await fileIo.open(uri, fileIo.OpenMode.READ_WRITE)let buffer = getContext(this).resourceManager.getMediaContentSync($r('app.media.img').id)// 將讀取的 ArrayBuffer 寫(xiě)入新的媒體文件let writeLen = await fileIo.write(file.fd, buffer.buffer)Logger.info('寫(xiě)入成功, 長(zhǎng)度=' + writeLen)await fileIo.close(file)} catch (err) {Logger.error('createAsset 失敗, message = ', err)}} else {Logger.error('SaveButtonOnClickResult createAsset 失敗')}})}.margin({ bottom: $r('app.float.default_10') }).width($r('app.float.default_336')).height($r('app.float.default_223')).borderRadius($r('app.float.default_24')).justifyContent(FlexAlign.SpaceAround).backgroundColor($r('app.color.start_window_background'))Column() {Text($r('app.string.select_photo')).width($r('app.float.default_302')).height($r('app.float.default_48')).lineHeight($r('app.float.default_22')).fontFamily('HarmonyHeiTi-Medium').fontSize($r('app.float.default_16')).fontWeight(500).textAlign(TextAlign.Start).fontColor($r('app.color.text_color'))Column() {if (!this.flag) {Image($r('app.media.ic_folder_add2')).width($r('app.float.default_24')).height($r('app.float.default_24')).objectFit(ImageFit.Contain)} else {Image(this.picture).width('100%').height('100%').borderRadius($r('app.float.default_24'))}}.width($r('app.float.default_312')).height($r('app.float.default_147')).borderRadius($r('app.float.default_16')).backgroundColor($r('app.color.picture_background')).onClick(async () => {await photoPickerGetUri().then(value => {this.flag = truethis.picture = value})}).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}.margin({ bottom: $r('app.float.default_10') }).backgroundColor($r('app.color.start_window_background')).width($r('app.float.default_336')).height($r('app.float.default_213')).borderRadius($r('app.float.default_24'))}.width('100%')}
}

關(guān)鍵代碼說(shuō)明:

  • 首先需要在圖庫(kù)中創(chuàng)建媒體資源,隨后將圖片讀取轉(zhuǎn)化為ArrayBuffer,最后再將該內(nèi)容寫(xiě)入在圖庫(kù)中創(chuàng)建的媒體資源中。
  • createAsset方法會(huì)返回在圖庫(kù)中新建的媒體資源的uri,只不過(guò)此時(shí)還是沒(méi)有內(nèi)容的空文件,然后需要將本地圖片文件的內(nèi)容讀取出來(lái),并寫(xiě)入該空文件,即可完成圖片保存到圖庫(kù)的操作。
  1. 從圖庫(kù)中選擇圖片進(jìn)行展示
// entry/src/main/ets/common/utils/PictureSaving.ets
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { BusinessError } from '@kit.BasicServicesKit'
import Logger from './Logger'// 定義 URI 數(shù)組,用于接收 PhotoViewPicker 選擇圖片后返回的 URI。
let uris: Array<string> = []/*** photoPickerGetUri.* 根據(jù) PhotoViewPicker 的 select 方法返回所選文件的 URI。* @return Promise<string>.*/
export async function photoPickerGetUri(): Promise<string> {try {let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions()PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPEPhotoSelectOptions.maxSelectNumber = 1let photoPicker = new photoAccessHelper.PhotoViewPicker()await photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {Logger.info('PhotoViewPicker.select 成功, 返回的 PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult))uris = PhotoSelectResult.photoUris}).catch((err: BusinessError) => {Logger.error('PhotoViewPicker.select 失敗, 錯(cuò)誤信息: ' + JSON.stringify(err))})} catch (error) {let err = error as BusinessErrorLogger.error('PhotoViewPicker 失敗, 錯(cuò)誤信息: ' + err.message)}return uris[0].toString()
}

關(guān)鍵代碼說(shuō)明:

  • 調(diào)用PhotoViewPicker的select方法來(lái)得到被選擇圖片的uri,即可完成讀取圖庫(kù)中圖片的信息。

(七) 用戶目錄的文件讀寫(xiě)

// entry/src/main/ets/common/utils/SavingAndSelectUserFile.ets
import { BusinessError } from '@kit.BasicServicesKit'
import { picker, fileIo } from '@kit.CoreFileKit'
import { buffer } from '@kit.ArkTS'
import Logger from './Logger'let uri: string = ''
// 獲取應(yīng)用文件路徑let message: string = ''/*** saveToUser.* 將內(nèi)容保存到用戶目錄文件中* @param content 要保存到用戶目錄文件中的內(nèi)容*/
export async function saveToUser(content: string) {try {let DocumentSaveOptions = new picker.DocumentSaveOptions()DocumentSaveOptions.newFileNames = ['test.txt']let documentPicker = new picker.DocumentViewPicker()documentPicker.save(DocumentSaveOptions).then((DocumentSaveResult: Array<string>) => {Logger.info('DocumentViewPicker.save 成功,返回的 uri: ' + JSON.stringify(DocumentSaveResult))uri = DocumentSaveResult[0]let file = fileIo.openSync(uri, fileIo.OpenMode.READ_WRITE)// 根據(jù)文件路徑打開(kāi)文件流。fileIo.writeSync(file.fd, content)}).catch((err: BusinessError) => {Logger.error('DocumentViewPicker.save 失敗,錯(cuò)誤信息: ' + JSON.stringify(err))})} catch (error) {let err: BusinessError = error as BusinessErrorLogger.error('DocumentViewPicker 失敗,錯(cuò)誤信息: ' + err.message)}
}/*** readUserFile.* 讀取用戶目錄文件中的內(nèi)容。* @return Promise<string>.*/
export async function readUserFile(): Promise<string> {try {let DocumentSelectOptions = new picker.DocumentSelectOptions()let documentPicker = new picker.DocumentViewPicker()await documentPicker.select(DocumentSelectOptions).then((DocumentSelectResult: Array<string>) => {Logger.info('DocumentViewPicker.select 成功,返回的 uri: ' + JSON.stringify(DocumentSelectResult))uri = DocumentSelectResult[0]let file = fileIo.openSync(uri, fileIo.OpenMode.READ_WRITE)let stat = fileIo.statSync(file.fd)let size = stat.sizelet buf = new ArrayBuffer(size)fileIo.readSync(file.fd, buf)let con = buffer.from(buf, 0)message = con.toString()Logger.info('DocumentViewPicker.select 成功,讀取的內(nèi)容: ' + message)return message}).catch((err: BusinessError) => {Logger.error('DocumentViewPicker.select 失敗,錯(cuò)誤信息: ' + JSON.stringify(err))})} catch (error) {let err = error as BusinessErrorLogger.error('DocumentViewPicker.select 失敗,錯(cuò)誤信息: ' + err.message)}return message
}

關(guān)鍵代碼說(shuō)明:

  • 在DocumentViewPicker提供的save方法的幫助下,可以向用戶目錄下創(chuàng)建一個(gè)文件并返回它的uri。
  • DocumentViewPicker提供的select方法,可以選擇想要訪問(wèn)的文件,并返回其uri。獲取到uri后,即可完成對(duì)文件的讀取,并將信息返回的操作。

二、文件管理知識(shí)點(diǎn)

Core File Kit(文件基礎(chǔ)服務(wù))為開(kāi)發(fā)者提供統(tǒng)一的文件訪問(wèn)和管理能力,涵蓋應(yīng)用文件和用戶文件,幫助用戶高效地查找、管理、備份各類文件,滿足多樣化的文件操作需求。

在文件分類上,Core File Kit將文件劃分為三類:應(yīng)用文件,如安裝包、資源、緩存等,由應(yīng)用擁有和管理;用戶文件,如圖片、視頻、文檔等,由登錄用戶所有;系統(tǒng)文件,包括系統(tǒng)資源、設(shè)備文件等,由系統(tǒng)統(tǒng)一管理,開(kāi)發(fā)者無(wú)需操作。按文件存儲(chǔ)位置,分為本地文件系統(tǒng)(本地設(shè)備或外置設(shè)備)和分布式文件系統(tǒng)(支持跨設(shè)備訪問(wèn))。

Core File Kit支持對(duì)應(yīng)用文件進(jìn)行查看、創(chuàng)建、讀寫(xiě)、刪除、復(fù)制、移動(dòng)、獲取屬性等操作;支持將應(yīng)用文件上傳至服務(wù)器,或從服務(wù)器下載資源;支持查詢應(yīng)用及文件系統(tǒng)的空間使用情況;支持通過(guò)URI或文件描述符方式,實(shí)現(xiàn)跨應(yīng)用文件分享;支持配置應(yīng)用數(shù)據(jù)的備份與恢復(fù);同時(shí),提供統(tǒng)一的用戶文件訪問(wèn)接口,便于用戶文件的選擇與保存;還支持在多設(shè)備間進(jìn)行文件訪問(wèn)和傳輸。

亮點(diǎn)方面,Core File Kit采用沙箱隔離機(jī)制,為每個(gè)應(yīng)用提供專屬的文件目錄空間,確保文件的隔離性與安全性。通過(guò)分享機(jī)制,開(kāi)發(fā)者可安全、高效地實(shí)現(xiàn)應(yīng)用間文件共享,確保數(shù)據(jù)一致性與傳輸安全,是構(gòu)建HarmonyOS文件能力的基礎(chǔ)組件之一。

(一) 應(yīng)用文件

應(yīng)用文件的所有者為應(yīng)用,包括應(yīng)用安裝文件、應(yīng)用資源文件、應(yīng)用緩存文件等。

1. 應(yīng)用沙箱目錄

應(yīng)用沙箱是一種以安全防護(hù)為目的的隔離機(jī)制,避免數(shù)據(jù)受到惡意路徑穿越訪問(wèn)。在這種沙箱的保護(hù)機(jī)制下,應(yīng)用可見(jiàn)的目錄范圍即為“應(yīng)用沙箱目錄”。

  • 對(duì)于每個(gè)應(yīng)用,系統(tǒng)會(huì)在內(nèi)部存儲(chǔ)空間映射出一個(gè)專屬的“應(yīng)用沙箱目錄”,它是“應(yīng)用文件目錄”與一部分系統(tǒng)文件(應(yīng)用運(yùn)行必需的少量系統(tǒng)文件)所在的目錄組成的集合。
  • 應(yīng)用沙箱限制了應(yīng)用可見(jiàn)的數(shù)據(jù)范圍。在“應(yīng)用沙箱目錄”中,應(yīng)用僅能看到自己的應(yīng)用文件以及少量的系統(tǒng)文件(應(yīng)用運(yùn)行必需的少量系統(tǒng)文件)。因此,本應(yīng)用的文件也不為其他應(yīng)用可見(jiàn),從而保護(hù)了應(yīng)用文件的安全。
  • 應(yīng)用可以在“應(yīng)用文件目錄”下保存和處理自己的應(yīng)用文件;系統(tǒng)文件及其目錄對(duì)于應(yīng)用是只讀的;而應(yīng)用若需訪問(wèn)用戶文件,則需要通過(guò)特定API同時(shí)經(jīng)過(guò)用戶的相應(yīng)授權(quán)才能進(jìn)行。

下圖展示了應(yīng)用沙箱下,應(yīng)用可訪問(wèn)的文件范圍和方式。

2. 應(yīng)用文件訪問(wèn)

應(yīng)用需要對(duì)應(yīng)用文件目錄下的應(yīng)用文件進(jìn)行查看、創(chuàng)建、讀寫(xiě)、刪除、移動(dòng)、復(fù)制、獲取屬性等訪問(wèn)操作,下面介紹具體方法。

  1. 接口說(shuō)明

開(kāi)發(fā)者通過(guò)基礎(chǔ)文件操作接口(ohos.file.fs)實(shí)現(xiàn)應(yīng)用文件訪問(wèn)能力,主要功能如下表所示。

接口名

功能

接口類型

支持同步

支持異步

access

檢查文件是否存在

方法

close

關(guān)閉文件

方法

copyFile

復(fù)制文件

方法

createStream

基于文件路徑打開(kāi)文件流

方法

listFile

列出文件夾下所有文件名

方法

mkdir

創(chuàng)建目錄

方法

moveFile

移動(dòng)文件

方法

open

打開(kāi)文件

方法

read

從文件讀取數(shù)據(jù)

方法

rename

重命名文件或文件夾

方法

rmdir

刪除整個(gè)目錄

方法

stat

獲取文件詳細(xì)屬性信息

方法

unlink

刪除單個(gè)文件

方法

write

將數(shù)據(jù)寫(xiě)入文件

方法

Stream.close

關(guān)閉文件流

方法

Stream.flush

刷新文件流

方法

Stream.write

將數(shù)據(jù)寫(xiě)入流文件

方法

Stream.read

從流文件讀取數(shù)據(jù)

方法

File.fd

獲取文件描述符

屬性

-

-

OpenMode

設(shè)置文件打開(kāi)標(biāo)簽

屬性

-

-

Filter

設(shè)置文件過(guò)濾配置項(xiàng)

類型

-

-

注意:使用基礎(chǔ)文件操作接口時(shí),耗時(shí)較長(zhǎng)的操作,例如:read、write等,建議使用異步接口,避免應(yīng)用崩潰。

  1. 開(kāi)發(fā)示例

在對(duì)應(yīng)用文件開(kāi)始訪問(wèn)前,開(kāi)發(fā)者需要獲取應(yīng)用文件路徑。

  • 新建并讀寫(xiě)一個(gè)文件

以下示例代碼演示了如何新建一個(gè)文件并對(duì)其讀寫(xiě)。

import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
import { buffer } from '@kit.ArkTS'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDirfunction createFile(): void {// 文件不存在時(shí)創(chuàng)建并打開(kāi)文件,文件存在時(shí)打開(kāi)文件let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)// 寫(xiě)入一段內(nèi)容至文件let writeLen = fs.writeSync(file.fd, "Try to write str.")console.info("The length of str is: " + writeLen)// 創(chuàng)建一個(gè)大小為1024字節(jié)的ArrayBuffer對(duì)象,用于存儲(chǔ)從文件中讀取的數(shù)據(jù)let arrayBuffer = new ArrayBuffer(1024)// 設(shè)置讀取的偏移量和長(zhǎng)度let readOptions: ReadOptions = {offset: 0,length: arrayBuffer.byteLength}// 讀取文件內(nèi)容到ArrayBuffer對(duì)象中,并返回實(shí)際讀取的字節(jié)數(shù)let readLen = fs.readSync(file.fd, arrayBuffer, readOptions)// 將ArrayBuffer對(duì)象轉(zhuǎn)換為Buffer對(duì)象,并轉(zhuǎn)換為字符串輸出let buf = buffer.from(arrayBuffer, 0, readLen)console.info("the content of file: " + buf.toString())// 關(guān)閉文件fs.closeSync(file)
}
  • 讀取文件內(nèi)容并寫(xiě)入到另一個(gè)文件

以下示例代碼演示了如何從一個(gè)文件讀寫(xiě)內(nèi)容到另一個(gè)文件。

import { fileIo as fs, ReadOptions, WriteOptions } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDirfunction readWriteFile(): void {// 打開(kāi)文件let srcFile = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)let destFile = fs.openSync(filesDir + '/destFile.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)// 讀取源文件內(nèi)容并寫(xiě)入至目的文件let bufSize = 4096let readSize = 0let buf = new ArrayBuffer(bufSize)let readOptions: ReadOptions = {offset: readSize,length: bufSize}let readLen = fs.readSync(srcFile.fd, buf, readOptions)while (readLen > 0) {readSize += readLenlet writeOptions: WriteOptions = {length: readLen}fs.writeSync(destFile.fd, buf, writeOptions)readOptions.offset = readSizereadLen = fs.readSync(srcFile.fd, buf, readOptions)}// 關(guān)閉文件fs.closeSync(srcFile)fs.closeSync(destFile)
}

使用讀寫(xiě)接口時(shí),需注意可選項(xiàng)參數(shù)offset的設(shè)置。對(duì)于已存在且讀寫(xiě)過(guò)的文件,文件偏移指針默認(rèn)在上次讀寫(xiě)操作的終止位置。

  • 以流的形式讀寫(xiě)文件

以下示例代碼演示了如何使用流接口讀取test.txt的文件內(nèi)容并寫(xiě)入到destFile.txt文件中。

import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDirasync function readWriteFileWithStream(): Promise<void> {// 創(chuàng)建并打開(kāi)輸入文件流let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+')// 創(chuàng)建并打開(kāi)輸出文件流let outputStream = fs.createStreamSync(filesDir + '/destFile.txt', "w+")let bufSize = 4096let readSize = 0let buf = new ArrayBuffer(bufSize)let readOptions: ReadOptions = {offset: readSize,length: bufSize}// 以流的形式讀取源文件內(nèi)容并寫(xiě)入到目標(biāo)文件let readLen = await inputStream.read(buf, readOptions)readSize += readLenwhile (readLen > 0) {const writeBuf = readLen < bufSize ? buf.slice(0, readLen) : bufawait outputStream.write(writeBuf)readOptions.offset = readSizereadLen = await inputStream.read(buf, readOptions)readSize += readLen}// 關(guān)閉文件流inputStream.closeSync()outputStream.closeSync()
}

使用流接口時(shí),需注意流的及時(shí)關(guān)閉。同時(shí)流的異步接口應(yīng)嚴(yán)格遵循異步接口使用規(guī)范,避免同步、異步接口混用。流接口不支持并發(fā)讀寫(xiě)。

  • 查看文件列表

以下示例代碼演示了如何查看文件列表。

import { fileIo as fs, Filter, ListFileOptions } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDir// 查看文件列表
function getListFile(): void {let listFileOption: ListFileOptions = {recursion: false,listNum: 0,filter: {suffix: [".png", ".jpg", ".txt"],displayName: ["test*"],fileSizeOver: 0,lastModifiedAfter: new Date(0).getTime()}}let files = fs.listFileSync(filesDir, listFileOption)for (let i = 0; i < files.length; i++) {console.info(`The name of file: ${files[i]}`)}
}
  • 使用文件流

以下示例代碼演示了如何使用文件可讀流,文件可寫(xiě)流。

import { fileIo as fs } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDirfunction copyFileWithReadable(): void {// 創(chuàng)建文件可讀流const rs = fs.createReadStream(`${filesDir}/read.txt`)// 創(chuàng)建文件可寫(xiě)流const ws = fs.createWriteStream(`${filesDir}/write.txt`)// 暫停模式拷貝文件。在拷貝數(shù)據(jù)時(shí),將原始數(shù)據(jù)暫停,然后將數(shù)據(jù)復(fù)制到另一個(gè)位置,// 適用于對(duì)數(shù)據(jù)完整性和一致性要求較高的場(chǎng)景rs.on('readable', () => {const data = rs.read()if (!data) {return}ws.write(data)})
}function copyFileWithData(): void {// 創(chuàng)建文件可讀流const rs = fs.createReadStream(`${filesDir}/read.txt`)// 創(chuàng)建文件可寫(xiě)流const ws = fs.createWriteStream(`${filesDir}/write.txt`)// 流動(dòng)模式拷貝文件。數(shù)據(jù)的讀取和寫(xiě)入是同時(shí)進(jìn)行的,不需要暫停原始數(shù)據(jù)的訪問(wèn),// 適用于對(duì)數(shù)據(jù)實(shí)時(shí)性要求較高的場(chǎng)景rs.on('data', (emitData) => {const data = emitData?.dataif (!data) {return}ws.write(data as Uint8Array)})
}
  • 使用文件哈希流

哈希流是一種數(shù)據(jù)傳輸和存儲(chǔ)技術(shù),可以將任意長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換為固定長(zhǎng)度的哈希值來(lái)驗(yàn)證數(shù)據(jù)的完整性和一致性。以下代碼演示了如何使用文件哈希處理接口(ohos.file.hash)來(lái)處理文件哈希流。

import { fileIo as fs } from '@kit.CoreFileKit'
import { hash } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDirfunction hashFileWithStream() {const filePath = `${filesDir}/test.txt`// 創(chuàng)建文件可讀流const rs = fs.createReadStream(filePath)// 創(chuàng)建哈希流const hs = hash.createHash('sha256')rs.on('data', (emitData) => {const data = emitData?.datahs.update(new Uint8Array(data?.split('').map((x: string) => x.charCodeAt(0))).buffer)})rs.on('close', async () => {const hashResult = hs.digest()const fileHash = await hash.hash(filePath, 'sha256')console.info(`hashResult: ${hashResult}, fileHash: ${fileHash}`)})
}

(二) 用戶文件

用戶文件是指登錄到該終端設(shè)備的用戶所擁有的文件,包括用戶私有的圖片、視頻、音頻、文檔等

1. 用戶文件uri

用戶文件uri是文件的唯一標(biāo)識(shí),在對(duì)用戶文件進(jìn)行訪問(wèn)與修改等操作時(shí)往往都會(huì)使用到uri,不建議開(kāi)發(fā)者解析uri中的片段用于業(yè)務(wù)代碼開(kāi)發(fā)。

uri類型可以歸納為文檔類uri和媒體文件uri兩類

  • 文檔類uri:由picker拉起文件管理器選擇或保存返回,以及通過(guò)fileAccess模塊獲取。
  • 媒體文件uri:由picker通過(guò)拉起圖庫(kù)選擇圖片或者視頻返回,通過(guò)photoAccessHelper模塊獲取圖片或者視頻文件的uri,以及通過(guò)userFileManager模塊獲取圖片、視頻或者音頻文件的uri。

2. 選擇用戶文件

用戶需要分享文件、保存圖片、視頻等用戶文件時(shí),開(kāi)發(fā)者可以通過(guò)系統(tǒng)預(yù)置的文件選擇器(FilePicker),實(shí)現(xiàn)該能力。通過(guò)Picker訪問(wèn)相關(guān)文件,將拉起對(duì)應(yīng)的應(yīng)用,引導(dǎo)用戶完成界面操作,接口本身無(wú)需申請(qǐng)權(quán)限。picker獲取的uri只具有臨時(shí)權(quán)限,獲取持久化權(quán)限需要通過(guò)FilePicker設(shè)置永久授權(quán)方式獲取。

根據(jù)用戶文件的常見(jiàn)類型,選擇器(FilePicker)分別提供以下選項(xiàng):

  • PhotoViewPicker:適用于圖片或視頻類型文件的選擇與保存。
  • DocumentViewPicker:適用于文件類型文件的選擇與保存。DocumentViewPicker對(duì)接的選擇資源來(lái)自于FilePicker, 負(fù)責(zé)文件類型的資源管理,文件類型不區(qū)分后綴,比如瀏覽器下載的圖片、文檔等,都屬于文件類型。
  • AudioViewPicker:適用于音頻類型文件的選擇與保存。AudioViewPicker目前對(duì)接的選擇資源來(lái)自于FilePicker。
  1. 選擇圖片或視頻類文件

圖庫(kù)選擇器對(duì)象,用來(lái)支撐選擇圖片/視頻等用戶場(chǎng)景。在使用前,需要先創(chuàng)建PhotoViewPicker實(shí)例。

let photoPicker = new photoAccessHelper.PhotoViewPicker()
  • 用法一
select(option?: PhotoSelectOptions) : Promise<PhotoSelectResult>

通過(guò)選擇模式拉起photoPicker界面,用戶可以選擇一個(gè)或多個(gè)圖片/視頻。接口采用promise異步返回形式,傳入可選參數(shù)PhotoSelectOptions對(duì)象,返回PhotoSelectResult對(duì)象。示例如下:

import { BusinessError } from '@kit.BasicServicesKit'
async function example01() {try {let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions()PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;PhotoSelectOptions.maxSelectNumber = 5let photoPicker = new photoAccessHelper.PhotoViewPicker()photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult))}).catch((err: BusinessError) => {console.error('PhotoViewPicker.select failed with' + `err: ${err.code}, ${err.message}`)})} catch (error) {let err: BusinessError = error as BusinessError;console.error('PhotoViewPicker failed with' + `err: ${err.code}, ${err.message}`)}
}
  • 用法二
select(option: PhotoSelectOptions, callback: AsyncCallback<PhotoSelectResult>
): void

通過(guò)選擇模式拉起photoPicker界面,用戶可以選擇一個(gè)或多個(gè)圖片/視頻。接口采用callback異步返回形式,傳入?yún)?shù)PhotoSelectOptions對(duì)象,返回PhotoSelectResult對(duì)象。示例如下:

import { BusinessError } from '@kit.BasicServicesKit'
async function example02() {try {let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions()PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPEPhotoSelectOptions.maxSelectNumber = 5let photoPicker = new photoAccessHelper.PhotoViewPicker();photoPicker.select(PhotoSelectOptions, (err: BusinessError, PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {if (err) {console.error('PhotoViewPicker.select failed with' + `err: ${err.code}, ${err.message}`)return}console.info('PhotoViewPicker.select successfully,PhotoSelectResult uri:' + JSON.stringify(PhotoSelectResult));})} catch (error) {let err: BusinessError = error as BusinessError;console.error('PhotoViewPicker failed with' + `err: ${err.code}, ${err.message}`);}
}
  • 用法三
select(callback: AsyncCallback<PhotoSelectResult>) : void

通過(guò)選擇模式拉起photoPicker界面,用戶可以選擇一個(gè)或多個(gè)圖片/視頻。接口采用callback異步返回形式,返回PhotoSelectResult對(duì)象。示例如下:

import { BusinessError } from '@kit.BasicServicesKit'
async function example03() {try {let photoPicker = new photoAccessHelper.PhotoViewPicker()photoPicker.select((err: BusinessError, PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {if (err) {console.error('PhotoViewPicker.select failed with' + `err: ${err.code}, ${err.message}`)return}console.info('PhotoViewPicker.select successfully,PhotoSelectResult uri:' + JSON.stringify(PhotoSelectResult))})} catch (error) {let err: BusinessError = error as BusinessErrorconsole.error(`PhotoViewPicker failed with err: ${err.code}, ${err.message}`)}
}
  1. 選擇文檔類文件
  • 導(dǎo)入選擇器模塊和基礎(chǔ)文件API模塊。
import  { picker } from '@kit.CoreFileKit'
import { fileIo as fs } from '@kit.CoreFileKit'
import { common } from '@kit.AbilityKit'
import { BusinessError } from '@kit.BasicServicesKit'
  • 創(chuàng)建文件類型、文件選擇選項(xiàng)實(shí)例。
const documentSelectOptions = new picker.DocumentSelectOptions()
// 選擇文檔的最大數(shù)目(可選)。
documentSelectOptions.maxSelectNumber = 5
// 指定選擇的文件或者目錄路徑(可選)。
documentSelectOptions.defaultFilePathUri = "file://docs/storage/Users/currentUser/test"
// 選擇文件的后綴類型['后綴類型描述|后綴類型'](可選) 若選擇項(xiàng)存在多個(gè)后綴名,
// 則每一個(gè)后綴名之間用英文逗號(hào)進(jìn)行分隔(可選),后綴類型名不能超過(guò)100,
// 選擇所有文件:'所有文件(*.*)|.*'。
documentSelectOptions.fileSuffixFilters = ['圖片(.png, .jpg)|.png,.jpg', '文檔|.txt', '視頻|.mp4', '.pdf']
// 選擇是否對(duì)指定文件或目錄授權(quán),true為授權(quán),當(dāng)為true時(shí),defaultFilePathUri為必選參數(shù),
// 拉起文管授權(quán)界面;false為非授權(quán)(默認(rèn)為false),拉起常規(guī)文管界面(可選),僅支持2in1設(shè)備。
documentSelectOptions.authMode = false
  • 創(chuàng)建文件選擇器DocumentViewPicker實(shí)例。調(diào)用select()接口拉起FilePicker應(yīng)用界面進(jìn)行文件選擇。
let uris: Array<string> = []
// 請(qǐng)確保 getContext(this) 返回結(jié)果為 UIAbilityContext
let context = getContext(this) as common.Context 
// 創(chuàng)建文件選擇器實(shí)例
const documentViewPicker = new picker.DocumentViewPicker(context);
documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {//文件選擇成功后,返回被選中文檔的uri結(jié)果集。uris = documentSelectResultconsole.info('documentViewPicker.select to file succeed and uris are:' + uris)}).catch((err: BusinessError) => {console.error(`Invoke documentViewPicker.select failed,` + `code is ${err.code}, message is ${err.message}`)})
  • 待界面從FilePicker返回后,使用基礎(chǔ)文件API的fs.openSync接口通過(guò)uri打開(kāi)這個(gè)文件得到文件描述符(fd)。
let uri: string = ''
//這里需要注意接口權(quán)限參數(shù)是fs.OpenMode.READ_ONLY。
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY)
console.info('file fd: ' + file.fd)
  • 通過(guò)fd使用fs.readSync接口讀取這個(gè)文件內(nèi)的數(shù)據(jù)。
let buffer = new ArrayBuffer(4096)
let readLen = fs.readSync(file.fd, buffer)
console.info('readSync data to file succeed and buffer size is:' + readLen)
//讀取完成后關(guān)閉fd。
fs.closeSync(file)
  1. 選擇音頻類文件
  • 導(dǎo)入選擇器模塊和基礎(chǔ)文件API模塊。
import  { picker } from '@kit.CoreFileKit'
import { fileIo as fs } from '@kit.CoreFileKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'
  • 創(chuàng)建音頻類型文件選擇選項(xiàng)實(shí)例。
const audioSelectOptions = new picker.AudioSelectOptions()
  • 創(chuàng)建音頻選擇器AudioViewPicker實(shí)例。調(diào)用select()接口拉起FilePicker應(yīng)用界面進(jìn)行文件選擇。
let uris: string = ''
// 請(qǐng)確保 getContext(this) 返回結(jié)果為 UIAbilityContext
let context = getContext(this) as common.Context
const audioViewPicker = new picker.AudioViewPicker(context)
audioViewPicker.select(audioSelectOptions).then((audioSelectResult: Array<string>) => {//文件選擇成功后,返回被選中音頻的uri結(jié)果集。uris = audioSelectResult[0]console.info('audioViewPicker.select to file succeed and uri is:' + uris)}).catch((err: BusinessError) => {console.error('Invoke audioViewPicker.select failed'+ ` code is ${err.code}, message is ${err.message}`)})
  • 待界面從FilePicker返回后,可以使用基礎(chǔ)文件API的fs.openSync接口通過(guò)uri打開(kāi)這個(gè)文件得到文件描述符(fd)。
let uri: string = ''
//這里需要注意接口權(quán)限參數(shù)是fs.OpenMode.READ_ONLY。
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY)
console.info('file fd: ' + file.fd)
  • 通過(guò)fd可以使用基礎(chǔ)文件API的fs.readSync接口讀取這個(gè)文件內(nèi)的數(shù)據(jù)。
let buffer = new ArrayBuffer(4096)
let readLen = fs.readSync(file.fd, buffer)
console.info('readSync data to file succeed and buffer size is:' + readLen)
//讀取完成后關(guān)閉fd。
fs.closeSync(file)

3. 保存用戶文件

在從網(wǎng)絡(luò)下載文件到本地或?qū)⒁延杏脩粑募泶鏋樾碌奈募窂降葓?chǎng)景下,需要使用FilePicker提供的保存用戶文件的能力。

  1. 保存圖片或視頻類文件

當(dāng)用戶需要保存圖片、視頻等用戶文件到圖庫(kù)時(shí),無(wú)需在應(yīng)用中申請(qǐng)相冊(cè)管理模塊權(quán)限'ohos.permission.WRITE_IMAGEVIDEO',應(yīng)用可以通過(guò)安全控件或授權(quán)彈窗的方式,將用戶指定的媒體資源保存到圖庫(kù)中。

(1) 使用安全控件保存媒體庫(kù)資源

下面以使用安全控件創(chuàng)建一張圖片資源為例。開(kāi)發(fā)步驟如下:

  • 設(shè)置安全控件按鈕屬性。
  • 創(chuàng)建安全控件按鈕。
  • 調(diào)用MediaAssetChangeRequest.createImageAssetRequest和PhotoAccessHelper.applyChanges接口創(chuàng)建圖片資源。
import { photoAccessHelper } from '@kit.MediaLibraryKit'@Entry
@ComponentV2
struct Index {saveButtonOptions: SaveButtonOptions = {icon: SaveIconStyle.FULL_FILLED,text: SaveDescription.SAVE_IMAGE,buttonType: ButtonType.Capsule} // 設(shè)置安全控件按鈕屬性build() {Column() {SaveButton(this.saveButtonOptions) // 創(chuàng)建安全控件按鈕.onClick(async (event, result: SaveButtonOnClickResult) => {if (result == SaveButtonOnClickResult.SUCCESS) {try {let context = getContext()let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)// 需要確保fileUri對(duì)應(yīng)的資源存在let fileUri = 'file://com.example.temptest/data/storage' + '/el2/base/haps/entry/files/test.jpg';let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest= photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, fileUri)await phAccessHelper.applyChanges(assetChangeRequest)console.info('createAsset successfully, uri: ' + assetChangeRequest.getAsset().uri)} catch (err) {console.error('create asset failed with ' + `error: ${err.code}, ${err.message}`)}} else {console.error('SaveButtonOnClickResult create asset failed')}})}.width('100%')}
}

除了上述通過(guò)fileUri從應(yīng)用沙箱指定資源內(nèi)容的方式,開(kāi)發(fā)者還可以通過(guò)ArrayBuffer的方式添加資源內(nèi)容。

(2) 使用彈窗授權(quán)保存媒體庫(kù)資源

下面以彈窗授權(quán)的方式保存一張圖片資源為例。開(kāi)發(fā)步驟如下:

  • 指定待保存到媒體庫(kù)的位于應(yīng)用沙箱的應(yīng)用文件圖片uri。
  • 指定待保存照片的創(chuàng)建選項(xiàng),包括文件后綴和照片類型,標(biāo)題和照片子類型可選。
  • 調(diào)用showAssetsCreationDialog,基于彈窗授權(quán)的方式獲取的目標(biāo)媒體文件uri。
  • 將來(lái)源于應(yīng)用沙箱的照片內(nèi)容寫(xiě)入媒體庫(kù)的目標(biāo)uri。
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { fileIo } from '@kit.CoreFileKit'let context = getContext(this)
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)async function example() {try {// 指定待保存到媒體庫(kù)的位于應(yīng)用沙箱的圖片urilet srcFileUri = 'file://com.example.temptest/data/storage/'+ 'el2/base/haps/entry/files/test.jpg'let srcFileUris: Array<string> = [srcFileUri]// 指定待保存照片的創(chuàng)建選項(xiàng),包括文件后綴和照片類型,標(biāo)題和照片子類型可選let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [{title: 'test', // 可選fileNameExtension: 'jpg',photoType: photoAccessHelper.PhotoType.IMAGE,subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可選}]// 基于彈窗授權(quán)的方式獲取媒體庫(kù)的目標(biāo)urilet desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs)// 將來(lái)源于應(yīng)用沙箱的照片內(nèi)容寫(xiě)入媒體庫(kù)的目標(biāo)urilet desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY)let srcFile: fileIo.File = await fileIo.open(srcFileUri, fileIo.OpenMode.READ_ONLY)await fileIo.copyFile(srcFile.fd, desFile.fd)fileIo.closeSync(srcFile)fileIo.closeSync(desFile)console.info('create asset by dialog successfully')} catch (err) {console.error('failed to create asset by dialog successfully ' + `errCode is: ${err.code}, ${err.message}`)}
}
  1. 保存文檔類文件
  • 模塊導(dǎo)入。
import { picker } from '@kit.CoreFileKit'
import { fileIo as fs } from '@kit.CoreFileKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'
  • 配置保存選項(xiàng)。
// 創(chuàng)建文件管理器選項(xiàng)實(shí)例。
const documentSaveOptions = new picker.DocumentSaveOptions()
// 保存文件名(可選)。 默認(rèn)為空。
documentSaveOptions.newFileNames = ["DocumentViewPicker01.txt"]
// 保存文件類型['后綴類型描述|后綴類型'],選擇所有文件:'所有文件(*.*)|.*'(可選),
// 如果選擇項(xiàng)存在多個(gè)后綴(最大限制100個(gè)過(guò)濾后綴),默認(rèn)選擇第一個(gè)。
// 如果不傳該參數(shù),默認(rèn)無(wú)過(guò)濾后綴。
documentSaveOptions.fileSuffixChoices = ['文檔|.txt', '.pdf']
  • 創(chuàng)建文件選擇器DocumentViewPicker實(shí)例。調(diào)用save()接口拉起FilePicker界面進(jìn)行文件保存。
let uris: Array<string> = []
// 請(qǐng)確保 getContext(this) 返回結(jié)果為 UIAbilityContext
let context = getContext(this) as common.Context
const documentViewPicker = new picker.DocumentViewPicker(context)
documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {uris = documentSaveResultconsole.info('documentViewPicker.save to file succeed and uris are:' + uris)}).catch((err: BusinessError) => {console.error('Invoke documentViewPicker.save failed, ' + `code is ${err.code}, message is ${err.message}`)})
  • 待界面從FilePicker返回后,使用基礎(chǔ)文件API的fs.openSync接口,通過(guò)URI打開(kāi)這個(gè)文件得到文件描述符(fd)。
const uri = ''
//這里需要注意接口權(quán)限參數(shù)是fs.OpenMode.READ_WRITE。
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE)
console.info('file fd: ' + file.fd)
  • 通過(guò)(fd)使用基礎(chǔ)文件API的fs.writeSync接口對(duì)這個(gè)文件進(jìn)行編輯修改,編輯修改完成后關(guān)閉(fd)。
let writeLen: number = fs.writeSync(file.fd, 'hello, world')
console.info('write data to file succeed and size is:' + writeLen)
fs.closeSync(file)
  1. 保存音頻類文件
  • 模塊導(dǎo)入。
import { picker } from '@kit.CoreFileKit'
import { fileIo as fs } from '@kit.CoreFileKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'
  • 配置保存選項(xiàng)。
const audioSaveOptions = new picker.AudioSaveOptions()
// 保存文件名(可選) 
audioSaveOptions.newFileNames = ['AudioViewPicker01.mp3']
  • 創(chuàng)建音頻選擇器AudioViewPicker實(shí)例。調(diào)用save()接口拉起FilePicker界面進(jìn)行文件保存。
let uri: string = ''
// 請(qǐng)確保 getContext(this) 返回結(jié)果為 UIAbilityContext
let context = getContext(this) as common.Context
const audioViewPicker = new picker.AudioViewPicker(context);
audioViewPicker.save(audioSaveOptions).then((audioSelectResult: Array<string>) => {uri = audioSelectResult[0]console.info('audioViewPicker.save to file succeed and uri is:' + uri)}).catch((err: BusinessError) => {console.error(`Invoke audioViewPicker.save failed, code is ` + `${err.code}, message is ${err.message}`)})
  • 待界面從FilePicker返回后,可以使用基礎(chǔ)文件API的fs.openSync接口,通過(guò)URI打開(kāi)這個(gè)文件得到文件描述符(fd)。
//這里需要注意接口權(quán)限參數(shù)是fileIo.OpenMode.READ_WRITE。
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE)
console.info('file fd: ' + file.fd)
  • 通過(guò)(fd)使用基礎(chǔ)文件API的fs.writeSync接口對(duì)這個(gè)文件進(jìn)行編輯修改,編輯修改完成后關(guān)閉(fd)。
let writeLen = fs.writeSync(file.fd, 'hello, world')
console.info('write data to file succeed and size is:' + writeLen)
fs.closeSync(file)
  1. DOWNLOAD模式保存文件

該模式具備自動(dòng)在 Download/包名/ 路徑創(chuàng)建目錄、跳過(guò)文件選擇界面直接保存文件、并返回具備持久化權(quán)限的URI供用戶創(chuàng)建文件的能力。

  • 模塊導(dǎo)入。
import { fileUri, picker } from '@kit.CoreFileKit'
import { fileIo as fs } from '@kit.CoreFileKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'
  • 配置下載模式。
const documentSaveOptions = new picker.DocumentSaveOptions()
// 配置保存的模式為DOWNLOAD,若配置了DOWNLOAD模式,
// 此時(shí)配置的其他documentSaveOptions參數(shù)將不會(huì)生效。
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD
  • 保存到下載目錄。
let uri: string = ''
// 請(qǐng)確保 getContext(this) 返回結(jié)果為 UIAbilityContext
let context = getContext(this) as common.Context
const documentViewPicker = new picker.DocumentViewPiker(context)
const documentSaveOptions = new picker.DocumentSaveOptions()
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD;
documentViewPicker.save(documentSaveOptions ).then((documentSaveResult: Array<string>) => {uri = documentSaveResult[0];console.info('documentViewPicker.save succeed and uri is:' + uri)const testFilePath = new fileUri.FileUri(uri + '/test.txt').pathconst file = fs.openSync(testFilePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)fs.writeSync(file.fd, 'Hello HarmonyOS')fs.closeSync(file.fd);}).catch((err: BusinessError) => {console.error(`Invoke documentViewPicker.save failed, ` + `code is ${err.code}, message is ${err.message}`)})

http://www.risenshineclean.com/news/63822.html

相關(guān)文章:

  • 海原縣城鄉(xiāng)建設(shè)局網(wǎng)站線上渠道推廣怎么做
  • 新手做電商怎么做推廣排名seo
  • 新聞網(wǎng)站建設(shè)源碼今日國(guó)內(nèi)新聞大事20條
  • 無(wú)錫外貿(mào)網(wǎng)站制作公司搭建一個(gè)網(wǎng)站的流程
  • 網(wǎng)站建設(shè)維護(hù)保密協(xié)議seo需要掌握哪些技術(shù)
  • 網(wǎng)站移動(dòng)端做pc端的301跳轉(zhuǎn)湖南網(wǎng)站seo公司
  • 怎么做交易貓釣魚(yú)網(wǎng)站片多多可以免費(fèi)看電視劇嗎
  • 貴陽(yáng)做網(wǎng)站多少錢(qián)免費(fèi)網(wǎng)站seo優(yōu)化
  • 設(shè)置網(wǎng)站開(kāi)場(chǎng)動(dòng)畫(huà)合肥正規(guī)的seo公司
  • wordpress 微博圖標(biāo)關(guān)鍵詞優(yōu)化和seo
  • 網(wǎng)站開(kāi)發(fā)原則愛(ài)站小工具圣經(jīng)
  • 多商城入住網(wǎng)站建設(shè)seo外包
  • 定陶網(wǎng)站建設(shè)怎么制作網(wǎng)頁(yè)
  • 網(wǎng)站百度知道怎么做推廣營(yíng)銷軟文廣告
  • 網(wǎng)站的推廣方式組合網(wǎng)絡(luò)營(yíng)銷推廣渠道有哪些
  • 深圳網(wǎng)站建設(shè) 設(shè)計(jì)科技外貿(mào)網(wǎng)站建站和推廣
  • 網(wǎng)站搭建語(yǔ)言網(wǎng)絡(luò)營(yíng)銷成功案例介紹
  • 美橙互聯(lián)網(wǎng)站建設(shè)案例游戲推廣怎么快速拉人
  • 電商網(wǎng)站策劃書(shū)俄羅斯搜索引擎yandex推廣
  • 珠寶玉器監(jiān)測(cè)網(wǎng)站建設(shè)方案2024年2月疫情又開(kāi)始了嗎
  • php外貿(mào)網(wǎng)站模板如何開(kāi)發(fā)自己的小程序
  • wordpress 文檔導(dǎo)入樹(shù)枝seo
  • 酒店網(wǎng)站設(shè)計(jì)的目的和意義如何在百度上添加店鋪的位置
  • 有關(guān)網(wǎng)站建設(shè)的書(shū)網(wǎng)站提交收錄軟件
  • 來(lái)賓網(wǎng)站建設(shè)seo網(wǎng)站關(guān)鍵詞排名軟件
  • 網(wǎng)站怎么做才 吸引人友情鏈接的網(wǎng)站
  • 多用戶批發(fā)商城aso優(yōu)化平臺(tái)
  • 蘇州室內(nèi)設(shè)計(jì)學(xué)校濟(jì)南網(wǎng)站優(yōu)化
  • 廣州市政府網(wǎng)站建設(shè)概括電腦版百度
  • 營(yíng)銷型網(wǎng)站策劃方案網(wǎng)站源碼