Ant Design Mobile ImageUploader 组件预览图片失败

568 阅读1分钟

问题描述

在 Step 步骤条中使用 ImageUploader 组件上传图片后,切换到步骤二,再回到步骤一,发现步骤一中ImageUploader 组件预览图片失败,再切换回步骤二,发现步骤二中的ImageUploader 组件也是一样预览图片失败,如下图:

原因分析

ImageUploader 组件上传图片后,会生成一个 blob url 用来展示图片, blob url 是 URL.createObjectURL() 方法返回的一段带 hash 的url,会一直存储在内存中, 直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放。当切换到下一个步骤时,前一个步骤的组件被卸载,ImageUploader 组件创建的 blob url 在内存中被释放,ImageUploader 找不到改 blob url 在内存中的存储地址,因此无法展示图片。

解决方案

在切换回前一个步骤时,使用 URL.createObjectURL() 方法重新创建 blob url

export function Uploader(props) {
  const value = props.value;

  // 处理 value,解决 blob url 失效的问题
  
  // 在 Form 中使用 UploadImage 组件,从 props 中可以取到 value
  // 在 非 From 中使用 UploadImage 组件,props 中没有 value
  if (value) {
    props.value =
      value &&
      Array.isArray(value) &&
      value?.map(item => {
        if (item?.response?.file) {
          // createObjectURL返回一段带hash的url,并且一直存储在内存中,直到document触发了unload事件(例如:document close)或者执行revokeObjectURL来释放
          // 内存中释放了该 url 后,upload 组件不再展示图片,需要重新创建
          const objUrl = URL.createObjectURL(item.response.file);
          item.response.url = objUrl;
          item.response.fileURL = objUrl;
          item.response.downloadURL = objUrl;
          item.url = objUrl;
          item.downloadURL = objUrl;
          item.imgURL = objUrl;
        }
        return item;
      });
  }

  return (
    <ImageUploader
      maxCount={2}
      upload={file => {
        var fileUrl = URL.createObjectURL(file);

        console.log('uploading', file, fileUrl);
        
        props.onProgress && props.onProgress();
        const fileUploadConfig = ventureFileBrokerConfig();

        return fbupload({
          file,
          multipart: { host: fileUploadConfig.host, bizCode: fileUploadConfig.bizCode, path: fileUploadConfig.path },
        }).then(result => {
          console.log('image upload result', result);
          props.onSuccess() && props.onSuccess();
          // 图片上传没有返回 filename,让图片上传组件报错
          if (!result.filename) return { url: '' };

          return {
            file,
            key: result.filename,
            id: result.filename,
            url: fileUrl,
            downloadURL: fileUrl,
            thumbnailUrl: fileUrl,
            fileName: result.filename,
            name: result.filename,
            fileURL: fileUrl,
          };
        });
      }}
      accept="image/png, image/jpg, image/jpeg, image/gif, image/bmp, application/pdf"
      value={props.value}
      {...props}
    />
  );
}