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

Cordova Android插件:USB存储指定扩展名最新文件的权限与实现问题

解决Android 11+下USB存储文件访问权限问题及文件列表实现

一、权限处理(Android 11+)

  • MANAGE_EXTERNAL_STORAGE权限的局限性:Android 11及以上版本中,即便申请了该权限,访问USB OTG存储仍可能受限——USB存储属于可移动媒体,需要额外适配处理。
  • 正确的权限方案:
    1. 声明权限:在AndroidManifest.xml中添加对应权限
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
          android:minSdkVersion="30" />
      <!-- Android 13+ 媒体文件权限,非媒体文件仍依赖MANAGE_EXTERNAL_STORAGE -->
      <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
      <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" />
      <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" />
      
    2. 动态申请权限:
      • Android 10及以下版本,直接申请READ_EXTERNAL_STORAGE
      • Android 11-12版本,申请MANAGE_EXTERNAL_STORAGE并引导用户到系统设置开启:
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (!Environment.isExternalStorageManager()) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                Uri uri = Uri.fromParts("package", getContext().getPackageName(), null);
                intent.setData(uri);
                startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE);
            }
        } else {
            ActivityCompat.requestPermissions(getActivity(),
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                REQUEST_CODE_READ_STORAGE);
        }
        
    3. USB存储的合规访问方案:使用Storage Access Framework (SAF) 让用户手动选择USB存储目录,无需依赖MANAGE_EXTERNAL_STORAGE,更符合隐私规范:
      Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
      intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
      startActivityForResult(intent, REQUEST_CODE_PICK_USB_DIR);
      
      在onActivityResult中获取授权Uri后,可通过DocumentFile操作文件。

二、文件列表功能优化

针对你的需求,优化后的函数直接返回指定扩展名的最新文件,同时增强错误处理:

private File getLatestFileWithExtension(File directory, final String extension) {
    if (directory == null || !directory.exists() || !directory.canRead()) {
        Log.e("USBManager", "目录无效或无读取权限");
        return null;
    }

    try {
        return Files.walk(directory.toPath(), 1)
                .filter(p -> {
                    try {
                        // 大小写不敏感匹配扩展名,避免漏检
                        String fileName = p.getFileName().toString().toLowerCase();
                        return Files.isRegularFile(p) && Files.isReadable(p)
                                && fileName.endsWith(extension.toLowerCase());
                    } catch (Exception e) {
                        Log.w("USBManager", "跳过无法访问的文件: " + p.getFileName(), e);
                        return false;
                    }
                })
                // 按修改时间倒序排序,取第一个即为最新文件
                .sorted((p1, p2) -> {
                    try {
                        return Long.compare(Files.getLastModifiedTime(p2).toMillis(),
                                Files.getLastModifiedTime(p1).toMillis());
                    } catch (IOException e) {
                        return 0;
                    }
                })
                .map(Path::toFile)
                .findFirst()
                .orElse(null);
    } catch (IOException e) {
        Log.e("USBManager", "获取文件列表失败: " + e.getMessage(), e);
        return null;
    }
}

关键优化点

  • 直接返回最新单个文件,无需额外处理列表
  • 增加目录合法性前置检查,提前输出错误日志
  • 扩展名匹配支持大小写不敏感,提升兼容性
  • 对无法访问的文件打印警告,便于排查问题
  • 使用findFirst()替代全列表收集,优化性能

三、Cordova插件适配注意事项

  • 借助cordova-plugin-android-permissions插件处理动态权限申请逻辑
  • SAF跳转的结果需在Cordova的Activity中处理onActivityResult,并将结果回调到JavaScript层

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 15:17:33