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

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"也无效,请问问题出在哪里?如何突破此限制?

解决方案

问题根源

  1. Android 10+存储权限机制变更:从Android 10(API 29)开始,系统启用分区存储(Scoped Storage),WRITE_EXTERNAL_STORAGE权限不再允许直接访问公共Documents目录;requestLegacyExternalStorage="true"仅在Android 10中生效,Android 11+完全失效。
  2. NativeScript API使用偏差:knownFolders.externalDocuments()指向的是应用专属的外部存储目录,而非公共的/sdcard/Documents/,直接写入公共目录会被系统拦截。
  3. 自定义函数的缺陷:未动态申请存储权限,且缓冲区创建方式有误,同时不符合分区存储的访问规则。

具体解决步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:05:56