Image加载沙箱图片
===========

#### 问题现象

利用文件下载把网络图片下载到沙箱目录后，使用Image组件直接加载沙箱路径图片却无法显示，该如何解决？

#### 背景知识

*   [加载图片资源](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-graphics-display#加载图片资源)：Image支持加载存档图、多媒体像素图和可绘制描述符三种类型。
*   [文件下载](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-file-upload-download)：应用可以从网络服务器下载网络资源文件到本地应用文件目录。
*   [应用沙箱](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-sandbox-directory)：对于每个应用，系统会在内部存储空间映射出一个专属的“应用沙箱目录”。在应用沙箱保护机制下，应用无法获知除自身应用文件目录之外的其他应用或用户的数据目录位置及存在。
    *   沙箱路径：指应用在沙箱环境中的物理文件路径，是文件在设备存储上的绝对路径字符串。示例：“/data/storage/el2/base/haps/entry/files/xxx.png”。
    *   沙箱URI：指文件路径的URI表示形式，以file://开头，包含应用包名和沙箱路径信息，用于标识文件资源。示例：“file://com.example.app/data/storage/el2/base/haps/entry/files/xxx.png”。

#### 解决方案

Image组件不能直接传入应用沙箱路径，需要传入应用沙箱URI。在下载完成后不要把filePath直接赋值给downloadImage，而是需要转换。

可以利用[getUriFromPath](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fileuri#fileurigeturifrompath)方法把传入的路径path生成应用自己的uri，修改如下：

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

@Entry
@Component
struct Index2 {
  @State downloadImage: string = '';

  build() {
    Column({ space: 30 }) {
    _  // 这里直接加载无法显示_
      Image(this.downloadImage)
        .width(100);
      Button('Download Image')
        .onClick(() => {
          try {
            let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
            let filesDir = context.cacheDir;
        _    // 将字符串/xxx.png拼接到filesDir路径末尾，表示在应用缓存文件路径下创建一个名为xxx.png的文件_
            let filePath = filesDir + '/xxx.png';
            let res = fs.accessSync(filePath);
            if (res) {
              this.getUIContext().getPromptAction().showToast({
                message: '文件已存在'
              });
            } else {
              request.downloadFile(context, {
             _   // 此处地址实际使用过程中替换为真实地址_
                url: 'XXX.XXX.png',
                filePath: filePath
              }).then((downloadTask: request.DownloadTask) => {
                downloadTask.on('complete', () => {
                  console.info(\`downloadImage= ${filePath}\`);
                  this.downloadImage = fileUri.getUriFromPath(filePath);
                });
              }).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}\`);
          }
        });
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center);
  }
}

#### 常见FAQ

Q：为什么不推荐使用'file://'进行路径拼接？

A：用'file://'对路径进行拼接的时候，无法对特殊字符进行处理，导致图片显示失败，而getUriFromPath方法会对路径中的中文及非数字字母的特殊字符将会被编译成对应的ASCII码，拼接在URI中。

Q：服务卡片中利用fileuri.getUriFromPath进行转换后还是无法加载出图片，该如何解决？

A：可以参照[刷新本地图片和网络图片](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-ui-widget-image-update)进行改动，在创建卡片时读取出沙箱中图片文件的fd，然后传给卡片进行展示。