Image组件加载含有特殊字符的图片失败如何解决
========================

#### 问题现象

Image组件使用file://协议，加载沙箱路径中含有特殊字符的图片资源失败，图片无法显示。

#### 背景知识

*   [Image组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-image)图片的数据源，支持本地图片和网络图片，本文主要讲述本地图片的引用。两种引用方式可以参考Image组件[显示图片指南](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-graphics-display)。
*   Image组件支持三种类型的图片数据源，分别为[PixelMap](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-image-common#pixelmap)、[ResourceStr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#resourcestr)、[DrawableDescriptor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-image#drawabledescriptor10)。

#### 解决方案

Image组件使用file://路径前缀的字符串的路径格式加载应用沙箱中图片资源，例如【@%^\\\_,.jpg】这种文件名中有特殊符号的图片，需要将路径转换为URI，再放入Image组件显示。

1.  构造含有特殊符号命名的沙箱图片，从相册中选取一张图片保存到应用沙箱中，并使用特殊符号给图片命名【@%^\_,.jpg】，然后获取沙箱路径中图片对应的URI：
    
    _// 从相册选择图片_
    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE\_TYPE;
    photoSelectOptions.maxSelectNumber = 1;
    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    photoPicker.select(photoSelectOptions).then(async (photoSelectResult) => {
     _ // 将图片保存到应用沙箱，并用特殊符号给图片命名_
      if (photoSelectResult.photoUris.length) {
        let photoUri: Array<string> = photoSelectResult.photoUris;
        let pathDir = this.context.filesDir;
        let path = pathDir + '/@%^\_,.jpg';
        console.info(\`取出的uri: ${photoUri\[0\]}\`);
        let file = fs.openSync(photoUri\[0\], fs.OpenMode.READ\_ONLY);
        let file2 = fs.openSync(path, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
        fs.copyFileSync(file.fd, file2.fd);
        fs.closeSync(file);
        fs.closeSync(file2);
      _  // 将沙箱路径的path转换为URI_
        this.path = 'file://' + this.context.abilityInfo.bundleName + path;
        this.uri = fileUri.getUriFromPath(path);
        console.info(\`uri: ${this.uri}\`);
        console.info(\`path: ${this.path}\`);
      }
    });
    
2.  在显示界面设置两个Image组件，一个的图片资源是沙箱路径path，另一个是沙箱路径对应的URI：
    
    _// 显示沙箱路径URI的值_
    Text('使用沙箱路径URI加载图片')
      .margin({ top: 30, bottom: 20 });
    Text('uri: ' + this.uri).margin({bottom:16})
    _// 使用沙箱路径URI显示图片_
    Image(this.uri)
      .width('100%')
      .height(200)
      .borderColor('#000001')
      .border({ width: 1 })
      .margin({ bottom: 75 });
    _// 显示沙箱路径path的值_
    Text('使用沙箱路径path加载图片')
      .margin({ bottom: 20 });
    Text('path: ' + this.path).margin({bottom:16});
    _// 使用沙箱路径path显示图片_
    Image(this.path)
      .width('100%')
      .height(200)
      .borderColor('#000001')
      .border({ width: 1 });
    
    可以看到展示结果中，URI的图片可以正常显示，而path的无法显示。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/40/v3/4Qzl8ViUS0uX1ur7T0B3Vg/zh-cn_image_0000002628559670.png?HW-CC-KV=V1&HW-CC-Date=20260709T133923Z&HW-CC-Expire=86400&HW-CC-Sign=3268BEC7CEF2177A0E646C01BEE0FA4056FE12780E194084EDBD1539DEAC2FAD "点击放大")
    

完整示例代码如下：

import { fileIo as fs, fileUri } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct ImageSpecialCharacters {
  context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  @State uri: string = '';
  @State path: string = '';

  build() {
    Column() {
     _ // 显示沙箱路径URI的值_
      Text('使用沙箱路径URI加载图片')
        .margin({ top: 30, bottom: 20 });
      Text('uri: ' + this.uri).margin({bottom:16})
     _ // 使用沙箱路径URI显示图片_
      Image(this.uri)
        .width('100%')
        .height(200)
        .borderColor('#000001')
        .border({ width: 1 })
        .margin({ bottom: 75 });
    _  // 显示沙箱路径path的值_
      Text('使用沙箱路径path加载图片')
        .margin({ bottom: 20 });
      Text('path: ' + this.path).margin({bottom:16});
     _ // 使用沙箱路径path显示图片_
      Image(this.path)
        .width('100%')
        .height(200)
        .borderColor('#000001')
        .border({ width: 1 });
      Blank().height(20);
      Button('选择图片').fontSize(15).height(30)
        .onClick(async () => {
         _ // 从相册选择图片_
          let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
          photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE\_TYPE;
          photoSelectOptions.maxSelectNumber = 1;
          let photoPicker = new photoAccessHelper.PhotoViewPicker();
          photoPicker.select(photoSelectOptions).then(async (photoSelectResult) => {
           _ // 将图片保存到应用沙箱，并用特殊符号给图片命名_
            if (photoSelectResult.photoUris.length) {
              let photoUri: Array<string> = photoSelectResult.photoUris;
              let pathDir = this.context.filesDir;
              let path = pathDir + '/@%^\_,.jpg';
              console.info(\`取出的uri: ${photoUri\[0\]}\`);
              let file = fs.openSync(photoUri\[0\], fs.OpenMode.READ\_ONLY);
              let file2 = fs.openSync(path, fs.OpenMode.READ\_WRITE | fs.OpenMode.CREATE);
              fs.copyFileSync(file.fd, file2.fd);
              fs.closeSync(file);
              fs.closeSync(file2);
            _  // 将沙箱路径的path转换为URI_
              this.path = 'file://' + this.context.abilityInfo.bundleName + path;
              this.uri = fileUri.getUriFromPath(path);
              console.info(\`uri: ${this.uri}\`);
              console.info(\`path: ${this.path}\`);
            }
          });
        });
    }.justifyContent(FlexAlign.Center).width('92%').height('100%').margin({left:16,right:16});
  }
}

#### 总结

当文件名或文件路径存在特殊字符时，可以将路径转换为URI，再将URI传入组件中使用。