如何使用DownloadFileButton组件保存文件
============================

#### 问题现象

如何使用DownloadFileButton组件保存网络文件到Download公共目录中？

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/90/v3/oc8k5MgtSqi3eAmkemYh6A/zh-cn_image_0000002629059026.png?HW-CC-KV=V1&HW-CC-Date=20260709T134131Z&HW-CC-Expire=86400&HW-CC-Sign=F984A48D8D9A57AD9B485E7BDFE410A56185C8E53476215CCE3822AA3EB24991 "点击放大")

#### 背景知识

*   [下载文件按钮](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ohos-arkui-advanced-downloadfilebutton)，通过点击该下载按钮，可以获取到当前应用在Download公共目录中所属的存储路径。
*   [DocumentViewPicker](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-picker#documentviewpicker)文件选择器对象，用来支撑选择和保存各种格式文档。在使用前，需要先创建DocumentViewPicker实例。

#### 解决方案

点击下载文件按钮，获取当前应用在Download公共目录中所属的存储路径，将下载好的文件写入，具体步骤如下：

1.  在module.json5文件中配置INTERNET网络权限：
    
    "requestPermissions": \[
      {
        "name":"ohos.permission.INTERNET"
      }
    \]
    
2.  创建DownloadFileButton组件，示例代码如下：
    
    DownloadFileButton({
      contentOptions: {
        icon: DownloadIconStyle.FULL\_FILLED,
        text: DownloadDescription.DOWNLOAD
      },
      styleOptions: {
        iconSize: '16vp',
        layoutDirection: DownloadLayoutDirection.HORIZONTAL,
        fontSize: '16vp',
        fontStyle: FontStyle.Normal,
        fontWeight: FontWeight.Medium,
        fontFamily: 'HarmonyOS Sans',
        fontColor: '#ffffffff',
        iconColor: '#ffffffff',
        textIconSpace: '4vp'
      }
    })
      .markAnchor({ x: 0, y: 0 })
      .offset({ x: 0, y: 0 })
      .padding({
        top: '12vp',
        bottom: '12vp',
        left: '24vp',
        right: '24vp'
      })
      .backgroundColor('#007dff')
      .borderStyle(BorderStyle.Dotted)
      .borderWidth(0)
      .borderRadius('24vp')
    
3.  点击下载文件按钮，通过[request.downloadFile](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-request#requestdownloadfile9)创建下载任务，代码示例如下：
    
    request.downloadFile(context, {
      url: 'xxx.xxx.xxxx/xxxx.png', _// 替换为自有链接_
      filePath: sanPath
    })
    
4.  下载完成，通过DocumentViewPicker保存文件，代码示例如下：
    
    const documentSaveOptions = new picker.DocumentSaveOptions();
    _// 创建文件管理器保存选项实例_
    documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD;
    _// 保存文件_
    const documentViewPicker = new picker.DocumentViewPicker;
    documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
     _ // 获取到文件的URI后进行文件读取等操作_
      let uri = documentSaveResult\[0\];
      this.constUri = uri;
     _ // 把这里的path存起来，作为后续保存文件的path_
      let path: string = new fileUri.FileUri(uri).path;
      let filePath: string = \`${path}/${'test.png'}\`;
      console.info('pub uri:', uri);
      let sanFile: fs.File | null = null;
      let pubFile: fs.File | null = null;
      try {
     _   // 沙箱路径文件_
        let sanFile = fs.openSync(sanPath, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
        let pubFile = fs.openSync(filePath, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
      _  // 将文件从沙箱路拷贝到公共路径_
        fs.copyFileSync(sanFile.fd, pubFile.fd);
      } catch (e) {
        console.error('fs.openSync failed error is : ', JSON.stringify(e));
      } finally {
        if (sanFile !== null) {
          fs.closeSync(sanFile);
        }
        if (pubFile !== null) {
          fs.closeSync(pubFile);
        }
      }
      this.getUIContext().getPromptAction().showToast({
        message: '保存到本地成功',
        duration: 2000,
        showMode: promptAction.ToastShowMode.DEFAULT,
        bottom: 85
      });
    }).catch((err: BusinessError) => {
      console.error(\`Invoke documentPicker.select failed, message is ${err.message}\`);
    });
    
    完整代码示例如下：
    
    import common from '@ohos.app.ability.common';
    import fs from '@ohos.file.fs';
    import request from '@ohos.request';
    import { BusinessError } from '@ohos.base';
    import { fileUri, picker } from '@kit.CoreFileKit';
    import {
      DownloadDescription,
      DownloadFileButton,
      DownloadIconStyle,
      DownloadLayoutDirection,
      promptAction
    } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct Index {
      @State constUri: string = '';
    
      build() {
        Row() {
          Column() {
            DownloadFileButton({
              contentOptions: {
                icon: DownloadIconStyle.FULL\_FILLED,
                text: DownloadDescription.DOWNLOAD
              },
              styleOptions: {
                iconSize: '16vp',
                layoutDirection: DownloadLayoutDirection.HORIZONTAL,
                fontSize: '16vp',
                fontStyle: FontStyle.Normal,
                fontWeight: FontWeight.Medium,
                fontFamily: 'HarmonyOS Sans',
                fontColor: '#ffffffff',
                iconColor: '#ffffffff',
                textIconSpace: '4vp'
              }
            })
              .markAnchor({ x: 0, y: 0 })
              .offset({ x: 0, y: 0 })
              .padding({
                top: '12vp',
                bottom: '12vp',
                left: '24vp',
                right: '24vp'
              })
              .backgroundColor('#007dff')
              .borderStyle(BorderStyle.Dotted)
              .borderWidth(0)
              .borderRadius('24vp')
              .onClick(() => {
                console.info('DownloadFileButton Click');
                let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
                let sanPath = context.filesDir + '/' + Date.now() + '.png';
                try {
                  request.downloadFile(context, {
                    url: 'xxx.xxx.xxxx/xxxx.png', _// 替换为自有链接_
                    filePath: sanPath
                  })
                    .then((downloadTask: request.DownloadTask) => {
                    downloadTask.on('complete', () => {
                      console.info('download complete');
                      const documentSaveOptions = new picker.DocumentSaveOptions();
                      _// 创建文件管理器保存选项实例_
                      documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD;
                    _  // 保存文件_
                      const documentViewPicker = new picker.DocumentViewPicker;
                      documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
                       _ // 获取到文件的URI后进行文件读取等操作_
                        let uri = documentSaveResult\[0\];
                        this.constUri = uri;
                     _   // 把这里的path存起来，作为后续保存文件的path_
                        let path: string = new fileUri.FileUri(uri).path;
                        let filePath: string = \`${path}/${'test.png'}\`;
                        console.info('pub uri:', uri);
                        let sanFile: fs.File | null = null;
                        let pubFile: fs.File | null = null;
                        try {
                       _   // 沙箱路径文件_
                          let sanFile = fs.openSync(sanPath, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
                          let pubFile = fs.openSync(filePath, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
                       _   // 将文件从沙箱路拷贝到公共路径_
                          fs.copyFileSync(sanFile.fd, pubFile.fd);
                        } catch (e) {
                          console.error('fs.openSync failed error is : ', JSON.stringify(e));
                        } finally {
                          if (sanFile !== null) {
                            fs.closeSync(sanFile);
                          }
                          if (pubFile !== null) {
                            fs.closeSync(pubFile);
                          }
                        }
                        this.getUIContext().getPromptAction().showToast({
                          message: '保存到本地成功',
                          duration: 2000,
                          showMode: promptAction.ToastShowMode.DEFAULT,
                          bottom: 85
                        });
                      }).catch((err: BusinessError) => {
                        console.error(\`Invoke documentPicker.select failed, message is ${err.message}\`);
                      });
                    });
                  }).catch((err: BusinessError) => {
                    console.error(\`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}\`);
                  });
                } catch (e) {
                  console.error(e.message);
                }
              })
          }.width('100%')
        }.height('100%')
      }
    }