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

#### 问题现象

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

#### 背景知识

*   Image组件图片的数据源，支持本地图片和网络图片，本文主要讲述本地图片的引用。两种引用方式可以参考Image组件显示图片指南。
*   Image组件支持三种类型的图片数据源，分别为PixelMap、ResourceStr、DrawableDescriptor。

#### 解决方案

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的无法显示。
    

完整示例代码如下：

```
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传入组件中使用。