产品姐:什么破App,保存个图片都忽好忽坏的?

6,511 阅读3分钟

前言

产品姐:“怎么有的图片预览可以保存成功,有的却提示保存失败?”

我:“不能够呀,正常图片应该没问题的哇” Snipaste_2025-05-16_17-16-39.png

产品姐摇摇头:“那这个二维码你给我保存下。”

我长按——“图像保存失败”。

产品姐一脸坏笑:“Bug 转给你咯。”

我:“……那个,后端哥哥 ~ 你的base64二维码图片可以转一下嘛(卑微)”

后端哥:“转毛,转不了一点”

我: Snipaste_2025-05-16_17-13-43.png

无可奈何~~~~~花落去 似曾相识燕归来

来~~~~~~~来,键来-----------

image.png

image.png

问题场景复现

言归歪传,在我们的 UniApp + Vue3 + TypeScript 项目中,二维码是通过 Base64 形式传输的,例如:

form.qrCode = 'data:image/png;base64,iVBORw0K...';

原本我们使用 wd-img 组件,通过如下方式展示二维码:

<wd-img :width="'150rpx'" :height="'150rpx'" :src="form.qrCode" mode="aspectFit" :enable-preview="true"/>

✅ 普通图片:在 App 上预览时,长按可保存;

❌ Base64 图片:提示“保存失败”。

二维码保存失败.gif

改造目标

  • ✅ 支持点击 Base64 图片进入预览
  • ✅ 支持长按弹出菜单(如“保存图片”)
  • ✅ 选择保存后,手动将 Base64 转为本地文件并保存到相册

步骤 1:点击触发自定义预览

将原本的 :enable-preview="true" 移除,改为绑定 @click 手动处理。

<wd-img
  :width="'150rpx'"
  :height="'150rpx'"
  :src="form.qrCode"
  mode="aspectFit"
  @click.stop="imgPreview(form.qrCode)"
/>

💡 说明

  • 使用 @click.stop 阻止事件冒泡,防止误触。
  • 进入预览逻辑交由我们自己改造控制。

步骤 2:封装预览函数 imgPreview

使用 uni.previewImage 进行预览,同时通过 longPressActions 修改长按图片显示操作菜单,如不填默认为保存相册

使用 itemList自定义长按后的操作菜单,可修改为你想要的['发送给朋友', '保存图片', '收藏']等等之类的

export const imgPreview = (url: string) => {
  uni.previewImage({
    urls: [url],
    longPressActions: {
      itemList: ['保存图片'],
      success: function (data) {
        // 用户点击了“保存图片”
        if (data.tapIndex === 0) {
          saveBase64Img(url)
        }
      },
      fail: function (err) {
        console.log('预览长按失败:', err);
      }
    }
  });
};

💡 说明

  • tapIndex === 0 表示用户点击了“保存图片”。
  • Base64 图片并不能直接保存,需转换处理。

步骤 3:封装 Base64 保存函数 saveBase64Img

关键是利用 plus.nativeObj.Bitmap 创建位图对象,从 Base64 中生成真实图片文件。

export const saveBase64Img = (base64: string) => {
  const bitmap = new plus.nativeObj.Bitmap('base64img');
  bitmap.loadBase64Data(
    base64,
    () => {
      const fileName = `_doc/${Date.now()}.png`;
      bitmap.save(
        fileName,
        { overwrite: true },
        () => {
          const realPath = plus.io.convertLocalFileSystemURL(fileName);
          uni.saveImageToPhotosAlbum({
            filePath: realPath,
            success: () => {
              uni.showToast({ title: '保存成功', icon: 'none' });
              bitmap.clear();
            },
            fail: () => {
              uni.showToast({ title: '保存失败', icon: 'none' });
              bitmap.clear();
            }
          });
        },
        (err) => {
          console.error('图片保存失败:', err);
          bitmap.clear();
        }
      );
    },
    (err) => {
      console.error('base64 解析失败:', err);
      bitmap.clear();
    }
  );
};

二维码保存成功.gif

🔍 代码解析

  • bitmap.loadBase64Data(base64):将 Base64 转换为位图。
  • _doc/:保存至 App 的私有沙盒路径中,避免权限问题。
  • plus.io.convertLocalFileSystemURL():转换为可用于保存相册的真实路径。
  • bitmap.clear():释放内存,别忘了清理资源!

小结:

  • 🚫 Base64 图片在 App 中无法直接预览后长按保存。
  • ✅ 自定义预览 + 保存逻辑能完美绕过这个限制。
  • 🔑 关键三板斧:plus.nativeObj.Bitmap + _doc/ 路径 + convertLocalFileSystemURL()

PS:我用的UI组件库是Wot,另外img和image也同理适用


彩蛋时间 🎁

产品姐:“这么快改好了?顺便把图片长按转发/收藏/点赞也做了吧,应该都很快吧”

我:“很快,离职手续办得很快”

感谢阅读!

下次再见!🌈

Snipaste_2025-04-27_15-18-02.png