鸿蒙NEXT开发-文件服务&上传下载

注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下

如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识

目录

[1. 文件服务](#1. 文件服务)

[1.1 基本介绍](#1.1 基本介绍)

[1.2 文件基础服务的能力](#1.2 文件基础服务的能力)

[1.3 文件基础服务的亮点](#1.3 文件基础服务的亮点)

[2. 应用文件](#2. 应用文件)

[2.1 基本介绍](#2.1 基本介绍)

[2.2 新建并读写一个文件](#2.2 新建并读写一个文件)

[2.3 查看文件列表](#2.3 查看文件列表)

[3. 用户文件](#3. 用户文件)

[3.1 基本介绍](#3.1 基本介绍)

[3.2 选择用户文件](#3.2 选择用户文件)

[3.3 保存用户文件](#3.3 保存用户文件)

[4. 文件-上传下载](#4. 文件-上传下载)

[4.1 基本介绍](#4.1 基本介绍)

[4.2 上传应用文件](#4.2 上传应用文件)

[4.3 下载网络资源文件至应用文件目录](#4.3 下载网络资源文件至应用文件目录)


1. 文件服务

1.1 基本介绍

Core File Kit(文件基础服务)为开发者提供一套访问和管理应用文件和用户文件的能力。帮助用户更高效地管理、查找和备份各类文件,使用户能够轻松应对各种文件管理的需求。

先了解下鸿蒙里面文件的划分,分成了下面三种:

  • 应用文件 文件所有者为应用,比如应用的安装包,自己的资源文件等。
  • 用户文件 文件所有者为用户,比如用户自己的照片,录制的音视频等。
  • 系统文件 文件所有者为系统,比如系统的配置文件等。

和安卓差不多吧,应用文件就是私有文件,不需要权限,用户文件的访问需要动态申请权限。

1.2 文件基础服务的能力

  • 支持对应用文件进行查看、创建、读写、删除、移动、复制、获取属性等访问操作。
  • 支持应用文件上传到网络服务器和网络服务器下载网络资源文件到本地应用文件目录。
  • 支持获取当前应用的存储空间大小、指定文件系统的剩余空间大小和指定文件系统的总空间大小。
  • 支持应用分享文件给其它应用和使用其它应用分享的文件。
  • 支持应用接入数据备份恢复,在接入后,应用可通过修改配置文件定制备份恢复框架的行为,包括是否允许备份恢复、备份哪些数据。
  • 提供用户文件访问框架,用于开发者访问和管理用户文件。例如选择与保存用户文件。
  • 支持跨设备的文件访问和拷贝能力。

1.3 文件基础服务的亮点

  • 沙箱隔离:

访问和管理应用文件,对于每个应用,系统会在内部存储空间映射出一个专属的"应用沙箱目录",它是"应用文件目录"与一部分系统文件(应用运行必需的少量系统文件)所在的目录组成的集合。有以下优点:

    • 隔离性:应用沙箱提供了一个完全隔离的环境,使用户可以安全地访问应用文件。
    • 安全性:应用沙箱限制了应用可见的数据的最小范围,保护了应用文件的安全。
  • 应用分享:

应用之间可以通过分享URI(Uniform Resource Identifier)或文件描述符FD(File Descriptor)的方式,进行文件共享。有以下优点:

    • 便携性:应用之间进行文件分享,省去了用户在多个应用间切换的麻烦,简化了操作步骤,提高了效率。
    • 高效性:应用间的文件分享能够更快地完成文件的传输,减少了因多次跳转和等待而浪费的时间。
    • 数据一致性:应用间的文件分享能够确保数据的完整性和一致性,避免数据在传输过程中出现损坏或丢失的情况。
    • 安全性:应用间的文件分享可以确保文件的安全性,避免文件被非法获取或篡改。同时,通过文件授权访问的方式,可以进一步增强文件的安全性。

2. 应用文件

2.1 基本介绍

应用需要对应用文件目录下的应用文件进行查看、创建、读写、删除、移动、复制、获取属性等访问操作,我们可以通过"@ohos.file.fs"去操作。

2.2 新建并读写一个文件

import { common } from '@kit.AbilityKit';
import { fileIo as fs, ReadOptions } from '@kit.CoreFileKit';
import { buffer } from '@kit.ArkTS';

// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;

@Entry
  @Component
  struct Index {
    build() {
      Column() {
        Text('写入文件').onClick(() => {
          createFile()
        })
      }
      .height('100%')
        .width('100%')
    }
  }

function createFile(): void {
  // 新建并打开文件
  let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  // 写入一段内容至文件
  let writeLen = fs.writeSync(file.fd, "我是新文件");
  console.info("文件内容字节长度: " + writeLen);
  // 从文件读取一段内容
  let arrayBuffer = new ArrayBuffer(1024);
  let readOptions: ReadOptions = {
    offset: 0,
    length: arrayBuffer.byteLength
  };
  let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
  let buf = buffer.from(arrayBuffer, 0, readLen);
  console.info("文件内容输出 " + buf.toString());
  // 关闭文件
  fs.closeSync(file);
}

注意:应用文件对于普通用户在手机文件管理查看是看不到的,开发者可以通过@ohos.file.fs来查看

2.3 查看文件列表

import { common } from '@kit.AbilityKit';
import { fileIo as fs, ListFileOptions, ReadOptions } from '@kit.CoreFileKit';
import { buffer } from '@kit.ArkTS';

// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;

@Entry
  @Component
  struct Index {
    build() {
      Column() {
        Button('写入文件').onClick(() => {
          createFile()
        }).height('10%').width(100).margin(100)

        Button('查看文件列表').onClick(() => {
          getListFile()
        })
          .height('10%').width(100)
      }
      .height('100%')
        .width('100%')
    }
  }

function createFile(): void {
  // 新建并打开文件
  let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  // 写入一段内容至文件
  let writeLen = fs.writeSync(file.fd, "我是新文件");
  console.info("文件内容字节长度: " + writeLen);
  // 从文件读取一段内容
  let arrayBuffer = new ArrayBuffer(1024);
  let readOptions: ReadOptions = {
    offset: 0,
    length: arrayBuffer.byteLength
  };
  let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
  let buf = buffer.from(arrayBuffer, 0, readLen);
  console.info("文件内容输出 " + buf.toString());
  // 关闭文件
  fs.closeSync(file);
}

// 查看文件列表
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(`文件: ${files[i]}`);
  }
}

官方地址:

文档中心

3. 用户文件

3.1 基本介绍

用户文件:文件所有者为登录到该终端设备的用户,包括用户私有的图片、视频、音频、文档等。

  1. 用户文件存放在用户目录下,归属于该设备上登录的用户。
  2. 用户文件存储位置主要分为内置存储、外置存储。
  3. 应用对用户文件的创建、访问、删除等行为,需要提前获取用户授权,或由用户操作完成。

3.2 选择用户文件

用户需要分享文件、保存图片、视频等用户文件时,开发者可以通过系统预置的文件选择器(FilePicker),实现该能力。通过Picker访问相关文件,将拉起对应的应用,引导用户完成界面操作,接口本身无需申请权限。picker获取的uri只具有临时权限,获取持久化权限需要通过FilePicker设置永久授权方式获取。

根据用户文件的常见类型,选择器(FilePicker)分别提供以下选项:

  • PhotoViewPicker:适用于图片或视频类型文件的选择与保存(该接口在后续版本不再演进)。请使用PhotoAccessHelper的PhotoViewPicker来选择图片文件。请使用安全控件创建媒体资源

  • DocumentViewPicker:适用于文件类型文件的选择与保存。DocumentViewPicker对接的选择资源来自于FilePicker, 负责文件类型的资源管理,文件类型不区分后缀,比如浏览器下载的图片、文档等,都属于文件类型。

  • AudioViewPicker:适用于音频类型文件的选择与保存。AudioViewPicker目前对接的选择资源来自于FilePicker。

    import { picker } from '@kit.CoreFileKit';
    import { common } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { fileIo as fs } from '@kit.CoreFileKit';
    import { buffer } from '@kit.ArkTS';

    // 选择的文件uri
    let uri: string = ''
    let context = getContext(this) as common.Context; // 请确保 getContext(this) 返回结果为 UIAbilityContext

    @Entry
    @Component
    struct Index {
    build() {
    Column() {
    Button('选择文件').onClick(() => {
    selectFile()
    }).height('10%').width(100)

      }
      .height('100%')
      .width('100%')
    }
    

    }

    function selectFile(): void {
    const documentSelectOptions = new picker.DocumentSelectOptions();
    // 选择文档的最大数目(可选)
    documentSelectOptions.maxSelectNumber = 1;
    // 指定选择的文件或者目录路径(可选)
    documentSelectOptions.defaultFilePathUri = "file://docs/storage/Users/currentUser/";
    // 选择文件的后缀类型['后缀类型描述|后缀类型'](可选) 若选择项存在多个后缀名,则每一个后缀名之间用英文逗号进行分隔(可选),后缀类型名不能超过100,选择所有文件:'所有文件(.)|.*';
    documentSelectOptions.fileSuffixFilters = ['图片(.png, .jpg)|.png,.jpg', '文档|.txt', '视频|.mp4', '.pdf'];
    //选择是否对指定文件或目录授权,true为授权,当为true时,defaultFilePathUri为必选参数,拉起文管授权界面;false为非授权,拉起常规文管界面(可选)
    documentSelectOptions.authMode = true;
    // 创建文件选择器实例
    const documentViewPicker = new picker.DocumentViewPicker(context);
    documentViewPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    //文件选择成功后,返回被选中文档的uri结果集。
    uri = documentSelectResult[0];
    console.info('选择的文件路径是' + uri);
    //这里需要注意接口权限参数是fs.OpenMode.READ_ONLY。
    let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
    console.info('文件描述符: ' + file.fd);
    let arrayBuffer = new ArrayBuffer(4096);
    let readLen = fs.readSync(file.fd, arrayBuffer);
    console.info('读取文件内容成功,内容长度是:' + readLen);
    // 输出文件内容
    let buf = buffer.from(arrayBuffer, 0, readLen);
    console.log('文件内容:' + buf.toString())
    //读取完成后关闭fd。
    fs.closeSync(file);
    }).catch((err: BusinessError) => {
    console.error(Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message});
    })

    }

3.3 保存用户文件

在从网络下载文件到本地、或将已有用户文件另存为新的文件路径等场景下,需要使用FilePicker提供的保存用户文件的能力。Picker获取的URI只具有临时权限,获取持久化权限需要通过FilePicker设置永久授权方式获取。

对音频、图片、视频、文档类文件的保存操作类似,均通过调用对应Picker的save()接口并传入对应的saveOptions来实现。通过Picker访问相关文件,无需申请权限

import { picker } from '@kit.CoreFileKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
// 保存的文件选择的路径
let uri: string = ''
let context = getContext(this) as common.Context; // 请确保 getContext(this) 返回结果为 UIAbilityContext

@Entry
  @Component
  struct Index {
    build() {
      Column() {
        Button('保存文件').onClick(() => {
          selectFile()
        }).height('10%').width(100)

      }
      .height('100%')
        .width('100%')
    }
  }

function selectFile(): void {
  // 创建文件管理器选项实例
  const documentSaveOptions = new picker.DocumentSaveOptions();
  // 保存文件名(可选)
  documentSaveOptions.newFileNames = ["hello.txt"];
  // 保存文件类型['后缀类型描述|后缀类型'],选择所有文件:'所有文件(*.*)|.*'(可选) ,如果选择项存在多个后缀,默认选择第一个。
  documentSaveOptions.fileSuffixChoices = ['文档|.txt', '.pdf'];

  // 创建文件选择器实例。
  const documentViewPicker = new picker.DocumentViewPicker(context);
  //用户选择目标文件夹,用户选择与文件类型相对应的文件夹,即可完成文件保存操作。保存成功后,返回保存文档的URI。
  documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
    uri = documentSaveResult[0];
    console.info('需要保存到文件路径是:' + uri);

    //这里需要注意接口权限参数是fs.OpenMode.READ_WRITE。
    let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
    console.info('文件描述符: ' + file.fd);

    let writeLen: number = fs.writeSync(file.fd, 'hello, world');
    console.info('写入文件的内容长度是:' + writeLen);
    fs.closeSync(file);

  }).catch((err: BusinessError) => {
    console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
  })
}

官方地址:

文档中心

4. 文件-上传下载

4.1 基本介绍

应用可以将应用文件上传到网络服务器,也可以从网络服务器下载网络资源文件到本地应用文件目录。

4.2 上传应用文件

开发者可以使用上传下载模块(ohos.request)的上传接口将本地文件上传。文件上传过程使用系统服务代理完成,在api12中request.agent.create接口增加了设置代理地址参数,支持用户设置自定义代理地址。

当前上传应用文件功能,仅支持上传应用缓存文件路径(cacheDir)下的文件。

使用上传下载模块,需声明权限:ohos.permission.INTERNET。

注意:如果需要上传用户文件,这需要先获取用户文件的读权限,然后将用户文件复制到应用缓存文件路径下面,最后在上传。

import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';

// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let cacheDir = context.cacheDir;

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button('上传本地应用文件').onClick(() => {
        uploadFile()
      }).height('10%').width(100)
    }
    .height('100%')
    .width('100%')
  }
}

function uploadFile(): void {
  // 新建一个本地应用文件
  let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  fs.writeSync(file.fd, '测试上传本地应用文件');
  fs.closeSync(file);

  // 上传任务配置项
  let header = new Map<Object, string>();
  header.set('Authorization', 'eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE3MzE1NzkyNTgsInBob25lIjoiMTczMzMwMjY3MDcifQ.BegJWUrCojMDjMb7gmVJOUDhnhk2N8UKe6UwlVom1TNxu7AErdbDKBc1JRoi1_JMVOnrlVOayrMW_nrCBb5eMA');
  let files: Array<request.File> = [
  //uri前缀internal://cache 对应cacheDir目录
    {
      filename: 'test.txt',
      name: 'file', // 文件上传的key
      uri: 'internal://cache/test.txt',
      type: 'txt'
    }
  ]
  // 生成文件唯一id,通过后端将文件id和文件绑定起来,等会上传成功就可以根据文件id查询文件线上地址和所需要的文件信息
  const fileId = util.generateRandomUUID(true)
  let data: Array<request.RequestData> = [{ name: 'fileId', value: fileId }];
  let uploadConfig: request.UploadConfig = {
    url: 'http://10.10.164.154:8099/v1/user/upload/file',
    header: header,
    method: 'POST',
    files: files,
    data: data
  }

  // 将本地应用文件上传至网络服务器
  try {
    request.uploadFile(context, uploadConfig)
      .then((uploadTask: request.UploadTask) => {
        uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
          for (let i = 0; i < taskStates.length; i++) {
            console.info(`upload complete taskState: ${JSON.stringify(taskStates[i])}`);
          }
        });
      })
      .catch((err: BusinessError) => {
        console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
      })
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
  }


}

4.3 下载网络资源文件至应用文件目录

开发者可以使用上传下载模块(ohos.request)的下载接口将网络资源文件下载到应用文件目录。对已下载的网络资源文件,开发者可以使用基础文件IO接口(ohos.file.fs)对其进行访问,使用方式与应用文件访问一致。文件下载过程使用系统服务代理完成,在api12中request.agent.create接口增加了设置代理地址参数,支持用户设置自定义代理地址。

当前网络资源文件仅支持下载至应用文件目录。

使用上传下载模块,需声明权限:ohos.permission.INTERNET。

注意:如果需要在文件管理查看到当前下载的文件,这需要获取写文件权限,先将文件下载到应用文件,然后将文件复制到用户文件下面就好了。

import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { buffer } from '@kit.ArkTS';

// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;

@Entry
  @Component
  struct Index {
    build() {
      Column() {
        Button('下载文件').onClick(() => {
          downloadFile()
        }).height('10%').width(100)
      }
      .height('100%')
        .width('100%')
    }
  }

function downloadFile(): void {
  try {
    // 判断文件是否存在,存在就删除
    let path = context.filesDir + '/test.png';
    if (fs.accessSync(path)) {
      fs.unlinkSync(path);
    }
    request.downloadFile(context, {
      url: 'https://www.baidu.com/link?url=8bSKCy3talaE0oHR0GO9EG8sZDONS13KbKlvZnrqtzt4tVkqzJBRd8Y5-C1GdvaCoCDiGQ7nu_KnacTLiVa9QynIa9oLBrWKb1Up4ihYFew_h6cszJuRhAizXpSXltSrRgyYqG7nyMlkuRYC35ayzHUcia2f_8GgQwHsjhhVuGrynu3CU3iTdutYJOHMpRENT86kM8GsM5tG8Lm3yBzP96wK8Y5rpn4b6pg8B2FL4bPktCXbzFK98xzppImvDyPyQhy1I7bJMuoIQwZ6FSBrTVoGcKU2sOmSSNjVGz8Q6vHWEuDinZwTJKJPoN3bAfUIhmDIi52ROIn04pP3Y-K-Py42jiSkECBnAR5v5gMzIonNDjKUeBHmIXo66hVizILvxzXIhm_i1lnYIA7aqmEFvzkNNNSgyZc81lAvDxzyYAcjyNsAE3bYGZ0jQdYZdCkRH1BTKnjselsTeqbfAg-FaAHNUD37cMx-Bvf-0hyfIystYNuiLTd1yCmbneBnxMGfm5FPMzDjRN2t5V-dkDVqMGM-rDAOlp7jpqow-mlh4S9dFjSG8EaO2woJvzCJm2nNXyjHYvHt7MemuJF7ky70iuGARcKk7vrhQUh_K1HKNCG8sFA5_t1luUI8U2AJEOZe-eBdN5k7zVot_WdKGFxVk7eTltW4qxX-CkuwuBMeIUa&wd=&eqid=8fa7c822000043380000000467347fbe',
      filePath: filesDir + '/test.png'
    }).then((downloadTask: request.DownloadTask) => {
      downloadTask.on('complete', () => {
        console.info('download complete');
        let file = fs.openSync(filesDir + '/test.png', fs.OpenMode.READ_WRITE);
        let arrayBuffer = new ArrayBuffer(1024);
        let readLen = fs.readSync(file.fd, arrayBuffer);
        let buf = buffer.from(arrayBuffer, 0, readLen);
        console.info(`The content of file: ${buf.toString()}`);
        fs.closeSync(file);
      })
    }).catch((err: BusinessError) => {
      console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}`);
  }
}
相关推荐
抓鱼猫L2 小时前
跨平台开发 ArkUI-X
harmonyos
泡泡大魔王3 小时前
【鸿蒙开发】HarmonyOS Arkts怎么拿到接口返回的Json数据,console.log显示不全 超长的解决方案
华为·harmonyos
别说我什么都不会7 小时前
鸿蒙轻内核M核源码分析系列十七(3) 异常信息ExcInfo
操作系统·harmonyos
kirk_wang7 小时前
HarmonyOS NEXT通过关系型数据库实现数据的持久化
数据库·华为·harmonyos
轻口味8 小时前
HarmonyOS Next 计时器组件详解
华为·harmonyos
SameX9 小时前
HarmonyOS Next 中的容器组件——构建灵活页面布局
harmonyos
塞尔维亚大汉1 天前
OpenHarmony(鸿蒙南向)——平台驱动开发【PIN】
harmonyos·领域驱动设计
Huang兄1 天前
鸿蒙-自定义相机拍照
华为·harmonyos
鸿蒙开天组●1 天前
鸿蒙实战篇-解决报错提示“code:9568305 error: dependent module does not exist”
华为·编辑器·harmonyos·鸿蒙