show-menu-by-longpress仅适用于微信小程序原生image标签,且需HTTPS域名备案、图片≥200×200px、非base64/canvas等渲染方式;App/支付宝需权限预检+downloadFile下载再保存;H5端应依赖浏览器原生长按保存,禁用@longpress模拟。

微信小程序:show-menu-by-longpress 是最简路径,但有硬性前提
微信小程序里想让用户长按就唤出「保存图片」「识别图中二维码」菜单,show-menu-by-longpress 属性确实能一行搞定,但它只对原生 标签生效,且必须同时满足三个条件:
- 图片
src必须是 HTTPS 地址,且该域名已添加到「开发管理 → 域名配置 → request 合法域名」中; - 图片尺寸 ≥ 200×200px,过小会导致识别失败或菜单不出现;
- 不能是
base64、data:、canvas绘制内容或background-image—— 这些渲染方式微信一律视为“非可识别内容”,长按无响应。
常见错误是把带水印的海报用 canvas 拼好后塞进 的 src,结果长按没反应。正确做法是:海报背景走 canvas,二维码单独用一个 叠加在上方。
App 端和支付宝小程序:必须手动触发 + 权限预检
App(iOS/Android)和支付宝小程序不支持 show-menu-by-longpress,得靠 @longpress 绑定事件,再调用 uni.saveImageToPhotosAlbum。但直接调用大概率失败,因为权限没提前检查:
- iOS 首次调用会弹系统权限框,用户点拒绝后,后续调用静默失败,控制台无报错;
- Android 6.0+ 需动态申请
WRITE_EXTERNAL_STORAGE或适配分区存储,用户勾选“不再询问”后无法再次弹窗; - 支付宝小程序还得多一步:
my.authorize({scope: 'alipay.user.permission.writephotosalbum'})主动申请。
所以必须先跑一遍权限检查链:uni.getSetting → 判断 scope.writePhotosAlbum 是否为 granted → 否则用 uni.authorize 或 uni.openSetting 引导。别等 saveImageToPhotosAlbum 报 fail auth deny 才处理。
网络图片保存:downloadFile 是唯一可靠入口,getImageInfo 已不可靠
很多老教程教用 uni.getImageInfo 获取网络图本地路径再保存,但这在新版微信基础库(2.25.0+)和部分安卓机型上会失败,返回的 res.path 是空或非法路径。现在稳妥做法只有:
- 用
uni.downloadFile把图片拉到临时路径; - 严格校验
downloadResult.statusCode === 200,否则不往下走; - 把
downloadResult.tempFilePath传给uni.saveImageToPhotosAlbum。
注意:下载前仍要确保图片域名已在 request 合法域名列表里,否则 downloadFile 直接卡死在 fail net::ERR_CONNECTION_REFUSED。H5 端完全不走这套流程,uni.saveImageToPhotosAlbum 会报 is not a function,必须用平台判断隔开。
H5 端:别试图模拟长按,浏览器原生行为才是正解
H5 没有相册权限概念,也不存在「长按保存」的系统能力。你写的 touchstart + setTimeout 长按逻辑,在 Safari 和部分安卓浏览器里要么失效,要么和双指缩放、单击关闭冲突。真实可行的方案只有两个:
- 让用户自己长按图片——现代手机浏览器(包括微信内置浏览器)都会自动弹出「保存图片」菜单,无需代码干预;
- 需要主动触发下载时,用
@#@#@#@#@#@#@#@#@#@0,但要求href是同域 URL 或 Blob URL;跨域图片必须服务端加Access-Control-Allow-Origin: *,否则 CORS 阻断。
最常被忽略的一点:H5 下 @longpress 绑定在 上根本不会触发,它只是 Vue 的修饰符,底层没有对应事件。真要兼容 H5,就得把长按逻辑从交互层移除,只保留「点击预览」,把保存交给用户自己操作。











