Expo Android:从第三方应用打开文件时FileSystem.copyAsync权限被拒
问题解决:Android第三方应用分享PDF到Expo应用时的权限拒绝问题
核心问题分析
从KakaoTalk、Slack等应用通过content:// URI打开PDF到你的Expo应用时,调用FileSystem.copyAsync触发权限拒绝,原因是:
- 第三方应用的FileProvider未对外暴露(
exported=false),仅通过临时权限授予你的应用 - Expo的
FileSystem.copyAsync无法正确继承Activity的临时URI权限,导致访问失败 - 原Intent Filter缺少PDF类型匹配,第三方应用可能未正确传递完整权限
解决方案步骤
1. 完善Manifest的Intent Filter
添加PDF的MIME类型和扩展名匹配,确保第三方应用能正确触发你的应用并传递权限:
<intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <!-- 自定义Scheme保留 --> <data android:scheme="moonlight"/> <data android:scheme="com.corca.moonlight"/> <data android:scheme="exp+moonlight"/> <!-- 添加PDF匹配规则 --> <data android:mimeType="application/pdf"/> <data android:pathPattern=".*\\.pdf"/> <data android:host="*"/> </intent-filter>
2. 替换FileSystem.copyAsync为原生文件复制逻辑
FileSystem.copyAsync无法处理第三方content:// URI的临时权限,需通过原生模块用ContentResolver读取文件流:
原生模块代码(Android)
@ReactMethod public void copyContentUriToCache(String contentUriStr, Promise promise) { try { Uri uri = Uri.parse(contentUriStr); Context context = getReactApplicationContext(); InputStream inputStream = context.getContentResolver().openInputStream(uri); File cacheDir = context.getCacheDir(); File tempFile = new File(cacheDir, "temp_shared.pdf"); OutputStream outputStream = new FileOutputStream(tempFile); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } inputStream.close(); outputStream.close(); promise.resolve(tempFile.getAbsolutePath()); } catch (Exception e) { promise.reject("COPY_FAILED", e.getMessage()); } }
TypeScript调用代码
import { NativeModules } from 'react-native'; const { FileCopierModule } = NativeModules; async function copySharedPathToUri(contentUri: string) { try { return await FileCopierModule.copyContentUriToCache(contentUri); } catch (error) { console.error('文件复制失败:', error); throw error; } }
3. 修正Intent URI传递逻辑
确保URI未被错误编码,在+native-intent.ts中添加解码校验:
export function redirectSystemPath({ path, initial }: { path: string; initial: string }) { try { const decodedPath = decodeURIComponent(path); console.log("redirectSystemPath", decodedPath, initial); return `/?sharedPath=${encodeURIComponent(decodedPath)}`; } catch (err) { console.error('路径处理失败:', err); return "/"; } }
4. 配置必要权限
在app.json中添加Android权限声明(适配Android 13+):
{ "expo": { "android": { "permissions": [ "READ_MEDIA_DOCUMENTS", "READ_EXTERNAL_STORAGE" ] } } }
内容的提问来源于stack exchange,提问作者rudxor02
相关产品推荐
相关产品推荐

