微信小程序没有弹出相册权限申请窗口

时间:2024-04-16 22:24:36

在微信小程序开发中,将图片或者视频保存到用户手机是常见的业务需求之一。通过调用小程序的下载文件以及保存文件到相册的API才能完成,后者会向用户申请权限,一旦用户不小心拒绝,那么下次保存文件的时候将不再弹出权限申请窗口,显然这对用户会造成困扰。

提示:如果用户在首次权限申请中拒绝,则必须在小程序的设置模块打开该权限,不再弹窗操作。

因此,作为开发者如何通过代码去引导用户正确操作呢?

首先我们可以通过API(wx.getSetting)获取当前小程序的设置信息,查看当前的权限状态。

wx.getSetting({
  success: res => {
    console.log(res.authSetting)
  }
})
提示:authSetting 是一个对象,保存当权用户的所有权限设置,对象的 Key 为具体权限别名,Value 为授权状态,即 true 已经授权;false 为拒绝该权限,如果没有申请过授权,则不保存在该对象中。

权限别名请参考:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html

如果没有授权,可以调用授权API(wx.authorize)向用户发起授权,同样只有第一次授权限才会弹窗,如果被拒绝过,返回失败。

wx.getSetting({
  success: res => {
    // 保存到相册的授权别名
    res.scope = \'scope.writePhotosAlbum\'
    if (!res.authSetting[res.scope]) {
      // 申请授权
      wx.authorize({
        scope: res.scope,
        success: res => {
          // 同意或已经授权
        },
        fail() {
          // 用户拒绝授权
        }
      })
    }
  }
})

因此,当用户拒绝过授权后,只能引导用户打开设置界面,开启相关权限。打开设置的API(wx.openSetting)返回当前新的设置信息。

需要注意的是,wx.openSetting 无法直接调用,但可以在弹窗 wx.showModal 回调中打开。

wx.showModal({
  content: \'请允许小程序使用相册权限\',
  success: () => {
    wx.openSetting({
      success: result => {
        // 如果用户还是不肯授权,提示没有权限
        if (!result.authSetting[res.scope]) {
          wx.showToast({
            title: \'未允许该权限\'
          })
        }
      },
    })
  }
})

梳理一下,先获取设置信息,判断是否授权,如果没有或者拒绝过,然后引导用户打开设置界面开启相关权限。当然,即使如此,用户可能仍然选择不开启权限,那只能提示用户没用权限,无法保存图片或视频,否则我们进行下一步操作。

整个过程完全是一个异步的操作,所以在封装的代码的时候,可以用 Promise 去完成。

分解一下操作,先下载视频,然后判断是否有权限保存图片视频文件(包括引导用户开启相关权限),最后才是去保存,这一步难点在获取异步的操作,你得先知道用户最后有没有打开权限。

 

重点来了,业务代码封装。

 

下载图片视频,调用下载文件的API(wx.downloadFile)

function downloadFile(url, listener) {
  listener = listener || {}
  return new Promise((resolve, reject) => {
    listener.onStart && listener.onStart()
    const downloadTask = wx.downloadFile({
      url,
      success: res => {
        // 下载状态正常
        if (res.statusCode == 200) {
          resolve(res)
        } else {
          reject(res)
        }
      }
    })
    if (listener.onProgress) {
      downloadTask.onProgressUpdate(listener.onProgress)
    }
  })
}

上面封装的 downloadFile 函数接受两个参数,url 下载地址,listener 下载监听回调,对象类型,listener.onStart 开始下载回调,listener.onProgress 下载进度回调

 

保存到相册,继续封装,API(wx.saveVideoToPhotosAlbum)

function saveMediaToPhotosAlbum(url, listener) {
  downloadFile(url, listener).then(res => {
    wx.saveVideoToPhotosAlbum({
      filePath: res.tempFilePath,
      success: listener.onComplete,
      fail: listener.onComplete
    })
  })
}

 

调用很简单,如下

const url = \'http://example.com/xxx.mp4\'

saveMediaToPhotosAlbum(url, {
  onStart() {
    wx.showToast({
      title: \'开始下载\',
    })
  },
  onComplete(res) {
    wx.showToast({
      title: \'下载完成\',
    })
  },
  onProgress(res) {
    // 下载进度回调
    wx.showLoading({
      title: res.progress + "%",
    })
  }
})

 

测试一下,你会发现,开始下载=》下载中=》进度在走,下载完成会弹出保存到相册权限弹窗,当你点击允许后提示已经保存到xxx(安卓)。好像挺完美,按照预想的过程走完,但别忘了,上面提到的授权问题,一旦拒绝,再调用就无法成功保存。

 

所以,在下载完成后还要进行授权操作,如果授权成功再保存到相册,否则提示没有权限无法保存。

 

同样用 Promise 封装

function authorize(scope) {
  scope = \'scope.writePhotosAlbum\'
  return new Promise((resolve, reject) => {
    wx.getSetting({
      success: res => {
        if (!res.authSetting[scope]) {
          wx.authorize({
            scope,
            success: resolve,
            fail: () => wx.showModal({
              content: R.string.prompt_authorize_photos, // 请允许小程序使用相册权限
              success: () => wx.openSetting({
                success: res => {
                  if (res.authSetting[scope]) {
                    resolve(res.authSetting)
                  } else {
                    wx.showModal({ content: R.string.prompt_authorize_cancel }) // 未允许使用该权限
                    reject(res.authSetting)
                  }
                }
              })
            })
          })
        } else {
          resolve(res.authSetting)
        }
      }
    })
  })
}

因为是要在文件下载完成后先判断授权状态,所以调用授权的代码应该放在 downloadFile 函数中,继续改造

function downloadFile(url, listener) {
  listener = listener || {}
  return new Promise((resolve, reject) => {
    listener.onStart && listener.onStart()
    const downloadTask = wx.downloadFile({
      url,
      success: res => {
        // 下载状态正常
        if (res.statusCode == 200) {
          resolve(res)
        } else {
          reject(res)
        }
      }
    })
    if (listener.onProgress) {
      downloadTask.onProgressUpdate(listener.onProgress)
    }
  }).then(res => {
    return authorize().then(() => res).catch(() => {
      listener.onComplete()
      return Promise.reject(null)
    })
  })
}

到处就大功告成,赶紧去试试。