Nativescript写入Android外部存储遇权限问题,如何解决?
问题描述
希望将文件导出至/sdcard/Documents/或/storage/emulated/0/Documents/目录,已在应用声明中添加以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
尝试使用Nativescript的File类操作:
const extFolder: Folder = knownFolders.externalDocuments() const extOutPath = path.join(extFolder.path, '123.json'); const extOutfile = File.fromPath(extOutPath);
最后一行报错:
ERROR Error: java.io.IOException: Operation not permitted
还尝试编写基于原生Android Java的自定义函数(在Nativescript中可访问android.os.Environment和java.io.FileInputStream):
exportFileToExternalStorage(fullInputFilePath, targetPath, fileName) { request(targetPath + fileName).then((response) => { console.log(android.os.Environment.DIRECTORY_DOCUMENTS) try { var myInput = new java.io.FileInputStream( fullInputFilePath ); var myOutput = new java.io.FileOutputStream(targetPath + fileName); var buffer = java.lang.reflect.Array.newInstance( java.lang.Byte.class.getField("TYPE").get(null), 1024 ); var length; while ((length = myInput.read(buffer)) > 0) { myOutput.write(buffer, 0, length); } } catch (err) { console.info("Error", err); alert(err.message) } //Close the streams myOutput.flush(); myOutput.close(); myInput.close(); }) }
写入目标目录时仍遇相同错误,添加android:requestLegacyExternalStorage="true"也无效,请问问题出在哪里?如何突破此限制?
解决方案
问题根源
- Android 10+存储权限机制变更:从Android 10(API 29)开始,系统启用分区存储(Scoped Storage),
WRITE_EXTERNAL_STORAGE权限不再允许直接访问公共Documents目录;requestLegacyExternalStorage="true"仅在Android 10中生效,Android 11+完全失效。 - NativeScript API使用偏差:
knownFolders.externalDocuments()指向的是应用专属的外部存储目录,而非公共的/sdcard/Documents/,直接写入公共目录会被系统拦截。 - 自定义函数的缺陷:未动态申请存储权限,且缓冲区创建方式有误,同时不符合分区存储的访问规则。
具体解决步骤
1. 适配分区存储:使用系统文件选择器(推荐)
Android 11+要求访问公共目录必须通过系统文件选择器让用户手动确认,这是合规的官方方案:
import { Application } from "@nativescript/core"; export async function saveToPublicDocuments() { // 构造创建文件的Intent const intent = new android.content.Intent(android.content.Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(android.content.Intent.CATEGORY_OPENABLE); intent.setType("application/json"); intent.putExtra(android.content.Intent.EXTRA_TITLE, "123.json"); // 启动文件选择器并获取返回的Uri const uri = await new Promise((resolve, reject) => { const activity = Application.android.foregroundActivity || Application.android.startActivity; const requestCode = 1001; const callback = new android.app.ActivityResultCallback({ onActivityResult: (resultCode, data) => { if (resultCode === android.app.Activity.RESULT_OK && data != null) { resolve(data.getData()); } else { reject(new Error("用户取消操作")); } } }); activity.registerForActivityResult(new android.app.ActivityResultContracts.StartActivityForResult(), callback).launch(intent); }); // 通过Uri写入文件内容 const contentResolver = Application.android.context.getContentResolver(); const outputStream = contentResolver.openOutputStream(uri as android.net.Uri); const fileContent = JSON.stringify({ key: "value" }); // 替换为你的文件内容 outputStream.write(fileContent.getBytes()); outputStream.flush(); outputStream.close(); }
2. Android 10及以下兼容方案(可选)
如果需要兼容Android 10,需完成以下操作:
- 在
AndroidManifest.xml中添加:<application android:requestLegacyExternalStorage="true" ...> - 动态申请存储权限:
先调用此函数获取权限,再执行文件写入操作。import { permissions } from "@nativescript/core"; async function requestStoragePermission() { const status = await permissions.requestPermissions([ android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ]); return status[android.Manifest.permission.WRITE_EXTERNAL_STORAGE] === "granted"; }
3. 修复自定义函数错误(仅兼容Android 10及以下)
若坚持使用原生代码,需修正缓冲区创建方式并确保权限已动态申请:
import { permissions } from "@nativescript/core"; async function requestStoragePermission() { const status = await permissions.requestPermissions([ android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE ]); return status[android.Manifest.permission.WRITE_EXTERNAL_STORAGE] === "granted"; } export async function exportFileToExternalStorage(fullInputFilePath, fileName) { const hasPermission = await requestStoragePermission(); if (!hasPermission) { alert("需要授予存储权限"); return; } // 获取公共Documents目录的正确路径 const publicDocsDir = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOCUMENTS); const targetPath = new java.io.File(publicDocsDir, fileName).getAbsolutePath(); try { const myInput = new java.io.FileInputStream(fullInputFilePath); const myOutput = new java.io.FileOutputStream(targetPath); // 正确创建字节缓冲区 const buffer = new java.lang.Byte[1024]; let length; while ((length = myInput.read(buffer)) > 0) { myOutput.write(buffer, 0, length); } myOutput.flush(); myOutput.close(); myInput.close(); } catch (err) { console.info("Error", err); alert(err.message); } }
注意:此方法在Android 11+会因分区存储限制报错,仅建议用于低版本兼容。
内容的提问来源于stack exchange,提问作者Alex-1999
相关产品推荐
相关产品推荐

