You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ionic 3:通过Promise处理权限弹窗,等待用户授权后执行下载

问题:Ionic 3应用中存储权限弹窗触发时,下载函数提前进入错误分支

我正在开发一款Ionic 3应用,核心功能是下载图片并保存到指定目录,这部分功能本身已经实现了。但遇到一个问题:卸载重装应用后,当尝试使用之前创建过的同一个文件夹时,首次下载图片会触发Android的存储权限弹窗,但在用户点击“允许”之前,downloadImage函数的错误分支就已经执行了,直接弹出“Download Failed!”的提示。

以下是我当前使用的代码:

downloadImage(imageURL) {
  var folderName = 'FolderName';
  this.createDirectory(folderName).then(() => {
    const fileTransfer: FileTransferObject = this.transfer.create();
    const imageName = imageURL.split('/').pop();
    fileTransfer.download(imageURL, this.storageDirectory + 'FolderName/' + imageName).then((entry) => {
      const alertSuccess = this.alertCtrl.create({
        title: `Download Succeeded!`,
        subTitle: `${imageURL} was successfully downloaded to: ${entry.toURL()}`,
        buttons: ['Ok']
      });
      alertSuccess.present();
    }, (error) => {
      const alertFailure = this.alertCtrl.create({
        title: `Download Failed!`,
        subTitle: `${imageURL} was not successfully downloaded. Error code: ${error.code}`,
        buttons: ['Ok']
      });
      alertFailure.present();
    });
  });
}

createDirectory(folderName): Promise<Boolean> {
  return new Promise((resolve, reject) => {
    if(!this.platform.is('cordova')) {
      return false;
    }
    if (this.platform.is('ios')) {
      this.storageDirectory = this.file.externalRootDirectory;
    } else if(this.platform.is('android')) {
      this.storageDirectory = this.file.externalRootDirectory;
    } else {
      return false;
    }
    this.file.checkDir(this.storageDirectory, folderName).then((success) => {
      resolve(true);
    })
    this.file.createDir(this.storageDirectory, folderName, false)
      .then((success) => {
        resolve(true);
      })
      .catch((error) => {
        reject(false);
      });
  });
}

请问是否可以通过Promise或者其他方式延迟后续的下载执行,等用户点击“允许”权限后再尝试下载操作?


解决方案:先确认权限,再执行下载逻辑

这个问题我之前帮朋友排查过,核心就是你在没确认权限是否授予的情况下,就直接跑了文件操作代码——卸载重装后应用权限会重置,首次访问存储时系统弹权限框,但这时候你的createDirectory或者下载请求已经发出去了,因为权限没给直接报错,根本没等用户点“允许”。

给你一套完整的解决思路:

1. 添加权限检查与请求工具函数

首先安装cordova-plugin-android-permissions插件,用来处理Android的存储权限请求:

cordova plugin add cordova-plugin-android-permissions

然后在你的组件中声明插件,并编写权限处理函数:

declare var cordova: any;

// ... 其他组件代码

async requestStoragePermission(): Promise<boolean> {
  if (!this.platform.is('android')) {
    return true; // iOS或其他平台无需额外处理
  }

  const permissions = cordova.plugins.permissions;
  const requiredPermissions = permissions.READ_EXTERNAL_STORAGE | permissions.WRITE_EXTERNAL_STORAGE;

  return new Promise((resolve) => {
    // 先检查是否已有权限
    permissions.checkPermission(requiredPermissions, (status) => {
      if (status.hasPermission) {
        resolve(true);
      } else {
        // 无权限则弹出系统权限请求框
        permissions.requestPermissions(requiredPermissions, (status) => {
          resolve(status.hasPermission);
        }, () => {
          resolve(false);
        });
      }
    }, () => {
      resolve(false);
    });
  });
}

2. 改造下载函数,等待权限确认后执行

把downloadImage改成async函数,先调用权限请求函数,等用户处理完权限弹窗后再继续执行下载逻辑:

async downloadImage(imageURL) {
  // 先获取存储权限,无权限则直接提示用户
  const hasPermission = await this.requestStoragePermission();
  if (!hasPermission) {
    const alertPermission = this.alertCtrl.create({
      title: `Permission Required`,
      subTitle: `Storage permission is needed to download images. Please enable it in settings and try again.`,
      buttons: ['Ok']
    });
    alertPermission.present();
    return;
  }

  var folderName = 'FolderName';
  try {
    await this.createDirectory(folderName);
    const fileTransfer: FileTransferObject = this.transfer.create();
    const imageName = imageURL.split('/').pop();
    const entry = await fileTransfer.download(imageURL, this.storageDirectory + 'FolderName/' + imageName);
    
    const alertSuccess = this.alertCtrl.create({
      title: `Download Succeeded!`,
      subTitle: `${imageURL} was successfully downloaded to: ${entry.toURL()}`,
      buttons: ['Ok']
    });
    alertSuccess.present();
  } catch (error) {
    const alertFailure = this.alertCtrl.create({
      title: `Download Failed!`,
      subTitle: `${imageURL} was not successfully downloaded. Error code: ${error.code}`,
      buttons: ['Ok']
    });
    alertFailure.present();
  }
}

3. 修复目录创建函数的Promise逻辑

你的createDirectory函数存在逻辑问题:同时调用checkDir和createDir会导致Promise被多次resolve,应该先检查目录是否存在,不存在再创建:

createDirectory(folderName): Promise<boolean> {
  return new Promise((resolve, reject) => {
    if (!this.platform.is('cordova')) {
      reject(false);
      return;
    }

    if (this.platform.is('ios') || this.platform.is('android')) {
      this.storageDirectory = this.file.externalRootDirectory;
    } else {
      reject(false);
      return;
    }

    this.file.checkDir(this.storageDirectory, folderName)
      .then(() => {
        resolve(true); // 目录已存在,直接返回成功
      })
      .catch(() => {
        // 目录不存在,执行创建操作
        this.file.createDir(this.storageDirectory, folderName, false)
          .then(() => resolve(true))
          .catch(() => reject(false));
      });
  });
}

原理说明

通过async/await(本质是Promise的语法糖),我们可以让代码暂停在权限请求步骤,直到用户处理完系统权限弹窗——不管是点击“允许”还是“拒绝”,拿到明确结果后才继续执行后续的目录检查和下载操作,这样就不会出现权限弹窗还未处理,下载逻辑就提前报错的情况了。

另外,修复目录创建函数的逻辑,能避免潜在的Promise重复触发问题,让整个流程更稳定。

内容的提问来源于stack exchange,提问作者Stranger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:48:59