HarmonyOS鸿蒙Next中word、excel、ppt、pdf文件的下载及预览示例

HarmonyOS鸿蒙Next中word、excel、ppt、pdf文件的下载及预览示例

8 回复

在 HarmonyOS NEXT 中,word,excel,ppt,pdf 实现思路

  1. 使用 @ohos.request 模块下载文件 到应用沙箱路径;
  2. 使用 @kit.PreviewKit 或 WebView 组件 进行预览;
  3. 文件需保存为本地沙箱路径,不支持直接预览网络文件。

更多关于HarmonyOS鸿蒙Next中word、excel、ppt、pdf文件的下载及预览示例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


通过HarmonyOS提供的Preview Kit(文件预览服务)实现Excel文件预览。实现思路如下:

  1. 使用resourceManager.getRawFdSync获取resources/rawfile目录下文件的HAP包描述符(fd)。
let srcFileDescriptor = context.resourceManager.getRawFdSync('1.xls');
  1. 通过fs.statSync获取文件属性,调用isFile()判断是否为普通文件。
if (!fs.statSync(srcFileDescriptor.fd).isFile()) {
  console.error('Not a regular file');
  return;
}
  1. 通过UIAbilityContext获取沙箱地址filesDir,fs.openSync打开文件,readSync/writeSync执行数据读写,操作完成后调用closeSync释放资源。
let pathDir = context.filesDir;  // 通过UIAbilityContext获取沙箱地址filesDir
let filePath = pathDir + "/1.xls";
// 以同步方法打开文件或目录。若文件不存在,则创建文件/读写打开
let file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
let bufsize = 4096
let buf = new ArrayBuffer(bufsize) // 用于保存读取到的文件数据的缓冲区。
let off = 0, len = 0, readedLen = 0 // 动态调整目标文件的写入位置. 写入实际读取的字节数,累计已读取的总字节数

while (len = fs.readSync(srcFileDescriptor.fd, buf, { offset: srcFileDescriptor.offset + off, length: bufsize })) {
  readedLen += len
  fs.writeSync(file.fd, buf, { offset: off, length: len })
  off = off + len
  // 当剩余未读取的字节数小于当前分块大小时,调整bufsize为剩余大小,避免无效读取
  if ((srcFileDescriptor.length - readedLen) < bufsize) {
    bufsize = srcFileDescriptor.length - readedLen
  }
}
// 关闭文件或目录
fs.close(file.fd)
  1. 通过filePreview.openPreview传入文件预览信息,打开预览窗口。 注意:①文件预览信息mimeType参数必须和文件一致,不然会无法打开。例如xls对应application/vnd.ms-excel。详细参考:文件预览支持的文件类型。 ②文件预览信息uri参数file://com.example.myapplication中com.example.myapplication为应用包名,实际使用时需要替换为当前工程项目中的应用包名。
// 文件预览信息
private fileInfo: filePreview.PreviewInfo = {
  title: '1.xls', // 文件的标题名称
  uri: 'file://com.example.myapplication/data/storage/el2/base/haps/entry/files/1.xls', // 文件的uri,此处com.example.myapplication为包名,请按照应用的实际包名替换
  mimeType: 'application/vnd.ms-excel' // 文件(夹)的媒体资源类型
};

filePreview.openPreview(this.uiContext, this.fileInfo).then(() => {
console.info('openPreview success');
}).catch((err: BusinessError) => {
console.error('openPreview failed, err = ' + err.message);
});

完整示例代码如下:

import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import { filePreview } from '@kit.PreviewKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
  uiContext: Context = this.getUIContext().getHostContext() as common.Context;
  // 文件预览信息
  private fileInfo: filePreview.PreviewInfo = {
    title: '1.xls', // 文件的标题名称
    uri: 'file://com.example.myapplication/data/storage/el2/base/haps/entry/files/1.xls', // 文件的uri,此处com.example.myapplication为包名,请按照应用的实际包名替换
    mimeType: 'application/vnd.ms-excel' // 文件(夹)的媒体资源类型
  };

  copyFile() {
    // 获取resources/rawfile目录下rawfile文件所在HAP的文件描述符(fd)
    let srcFileDescriptor = this.context.resourceManager.getRawFdSync('1.xls');
    // 判断文件是否是普通文件。true:是普通文件;false:不是普通文件。
    if (!fs.statSync(srcFileDescriptor.fd).isFile()) {
      console.error('Not a regular file');
      return;
    }

    let pathDir = this.context.filesDir; // 通过UIAbilityContext获取沙箱地址filesDir
    let filePath = pathDir + "/1.xls";
    // 以同步方法打开文件或目录。若文件不存在,则创建文件/读写打开
    let file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
    let bufsize = 4096
    let buf = new ArrayBuffer(bufsize) // 	用于保存读取到的文件数据的缓冲区。
    let off = 0, len = 0, readLen = 0 // 动态调整目标文件的写入位置. 写入实际读取的字节数,累计已读取的总字节数

    while (len = fs.readSync(srcFileDescriptor.fd, buf, { offset: srcFileDescriptor.offset + off, length: bufsize })) {
      readLen += len
      fs.writeSync(file.fd, buf, { offset: off, length: len })
      off = off + len
      // 当剩余未读取的字节数小于当前分块大小时,调整bufsize为剩余大小,避免无效读取
      if ((srcFileDescriptor.length - readLen) < bufsize) {
        bufsize = srcFileDescriptor.length - readLen
      }
    }
    // 关闭文件或目录
    fs.close(file.fd)
  }

  build() {
    Row() {
      Column() {
        Button('传到沙箱')
          .onClick(() => {
            this.copyFile()
          })
          .margin({ bottom: 10 })
        Button('预览文件')
          .onClick(() => {
            filePreview.openPreview(this.uiContext, this.fileInfo).then(() => {
              console.info('openPreview success');
            }).catch((err: BusinessError) => {
              console.error('openPreview failed, err = ' + err.message);
            });
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

文件下载预览,具体参考:文件下载预览-关键场景示例-综合办公类行业实践-行业实践 - 华为HarmonyOS开发者

文件下载基础版本 重复下载相同文件会出错

import { qq } from '../common/data';
import { request } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';


@Entry
@Component
struct Index {
  @State progress: number = 0;

  build() {
    Column() {
      Button("下载文件")
        .onClick(() => {
         request.downloadFile(getContext(this),{
           url: qq,
           background: true,
         }).then((task:request.DownloadTask)=>{
            task.on("progress",(receivedSize: number, totalSize: number)=>{
              this.progress = Math.ceil((receivedSize / totalSize) * 100);
            })
           task.on("complete",()=>{
             this.progress = 100
             promptAction.showToast({
               message: "下载完成",
             })
           })
         })
        })

      Progress({ value: this.progress, total: 100, type: ProgressType.Capsule })
        .width(100)
        .height(50)
        .style({
          borderColor: Color.Blue,
          borderWidth: 1,
          content: this.progress + '%',
          font: { size: 13, style: FontStyle.Normal },
          fontColor: Color.Gray,
          enableScanEffect: false,
          showDefaultPercentage: true
        })
        .privacySensitive(true)
        .margin({ top: 30 })
    }
    .height('100%')
    .width('100%')
  }
}

文件下载(断点续传)+多线程+通知 使用。request.agent.

import { qq } from '../common/data';
import { request } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';


@Entry
@Component
struct Index {
  @State progress: number = 0;

  taskId:string = ""

  async aboutToAppear(): Promise<void> {
    let task = await request.agent.create(getContext(this),{
      action:request.agent.Action.DOWNLOAD,
      url: qq,
      //支持复写
      overwrite:true,
      //通知
      gauge:true,
      // //保存文件目录
      // saveas:getContext(this).cacheDir
    })
    task.on("progress",(progress:request.agent.Progress)=>{
      //sizes是多个文件的大小
      this.progress =  Math.ceil(progress.processed * 100 / progress.sizes[0])
    })
    task.on("completed",()=>{
      promptAction.showToast({
        message:"下载完成"
      })
    })
    this.taskId = task.tid

  }

  build() {
    Column() {
      Button("下载文件")
        .onClick(() => {
          promptAction.showToast({
            message:"开始下载"
          })
          request.agent.getTask(getContext(this),this.taskId).then((task:request.agent.Task)=>{
            task.start()
          })
        })
      Button("暂停")
        .onClick(() => {
          promptAction.showToast({
            message:"暂停下载"
          })
          request.agent.getTask(getContext(this),this.taskId).then((task:request.agent.Task)=>{
            task.pause()
          })
        })
      Button("继续")
        .onClick(() => {
          promptAction.showToast({
            message:"继续下载"
          })
          request.agent.getTask(getContext(this),this.taskId).then((task:request.agent.Task)=>{
            task.resume()
          })
        })
      Progress({ value: this.progress, total: 100, type: ProgressType.Capsule })
        .width(100)
        .height(50)
        .style({
          borderColor: Color.Blue,
          borderWidth: 1,
          content: this.progress + '%',
          font: { size: 13, style: FontStyle.Normal },
          fontColor: Color.Gray,
          enableScanEffect: false,
          showDefaultPercentage: true
        })
        .privacySensitive(true)
        .margin({ top: 30 })
    }
    .height('100%')
    .width('100%')
  }
}

Preview Kit 预览

PDF常见的预览与下载一般都用这个:PDF下载和预览-行业常见问题-运动健康类行业实践-行业实践 - 华为HarmonyOS开发者
其他的文件可以引导用户使用WPS打开

我都是直接跳转到浏览器,然后用户在随意用WPS等工具打开。

HarmonyOS Next中,文件下载可通过网络模块实现,预览使用系统内置能力。下载示例:调用@ohos.net.http发起请求,获取文件流后通过@ohos.file.fs写入应用沙箱路径。预览通过want隐式启动系统文件查看器,指定文件URI和MIME类型(如application/pdf)。系统自动匹配可用应用打开文档,无需额外处理格式兼容。

在HarmonyOS Next中,处理Word、Excel、PPT和PDF文件的下载与预览可以通过以下方式实现:

  1. 文件下载
    使用@ohos.net.http模块发起网络请求获取文件数据,结合@ohos.file.fs将数据写入本地存储路径。示例代码:

    // 发起下载请求并保存文件
    const filePath = '本地存储路径/文件名.docx';
    // 实现下载逻辑
    
  2. 文件预览
    对于PDF,可使用@ohos.file.picker选择文件后通过系统能力打开;Office文件需依赖第三方库或转换为PDF预览。目前HarmonyOS Next原生支持PDF预览,但Office格式需集成适配库。

注意:实际开发中需处理权限申请(如网络和存储访问)及文件类型匹配。推荐使用ArkTS编写,确保兼容HarmonyOS Next的API版本。

回到顶部