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

Storage Access Framework云存储保存空文件无法读取问题排查

云存储通过SAF保存文件为空且读取失败问题排查

问题现象

  • 设备本地存储可正常保存、读取文件,但Google Drive、NextCloud中保存的文件为0字节
  • 读取Google Drive上的空文件触发IOException;读取NextCloud上的空文件时抛出NetworkOnMainThreadException
  • 测试设备(Android 11 Galaxy A10)已安装OneDrive,但文件选择器未显示该选项

相关代码

文件选择器代码

Intent intentCreate = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intentCreate.addCategory(Intent.CATEGORY_OPENABLE);             
intentCreate.setType("application/json");
intentCreate.putExtra(Intent.EXTRA_TITLE, fileNameGlobal + JSON_MY_FILE_EXT);
startActivityForResult(intentCreate, REQUEST_CODE);

文件写入代码

static public void writeNewTextDocumentInUri(final Activity activity, final Uri uri, final String mimetype, final String text) {
    final ContentResolver resolver = activity.getContentResolver();
    boolean result=true;
    try {
        ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w");
        FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
        OutputStreamWriter writer = new OutputStreamWriter(fos);
        writer.write(text);//the same using the append method
        writer.flush();
        writer.close();
        result = true;
    } catch (FileNotFoundException e) {
        result = false;
    } catch (IOException e) {
        result = false;
    }
    if (!result) openDialog();
}

文件读取代码

static public String readTextDocument(Activity activity, String fileUri) {
    ContentResolver contentResolver;
    contentResolver = activity.getContentResolver();
    InputStream is;

    try {
        is = contentResolver.openInputStream(Uri.parse(fileUri));
    } catch (FileNotFoundException e) {
        return null;
    }

    if (is == null) return null;

    InputStreamReader isr = new InputStreamReader(is);

    String text = new String();

    try {
        BufferedReader br = new BufferedReader(isr);
        String line;

        while ((line = br.readLine()) != null) {
            text = text + line + "\n";
        }
        br.close();
    } catch (IOException e) {
        return null;
    }

    return text;
}

NextCloud读取错误日志

android.os.NetworkOnMainThreadException
at android.os.Parcel.createExceptionOrNull(Parcel.java:2395)
at android.os.Parcel.createException(Parcel.java:2369)
at android.os.Parcel.readException(Parcel.java:2352)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:153)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:781)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1993)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1808)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1485)

问题原因及解决方案

1. 写入文件未完整释放资源

云存储的ContentProvider需要明确关闭ParcelFileDescriptor才能确保数据提交到云端,当前代码仅关闭了writer,未关闭pfd和fos,导致数据未被持久化。

修改写入代码,添加资源关闭逻辑:

static public void writeNewTextDocumentInUri(final Activity activity, final Uri uri, final String mimetype, final String text) {
    final ContentResolver resolver = activity.getContentResolver();
    boolean result = true;
    ParcelFileDescriptor pfd = null;
    FileOutputStream fos = null;
    OutputStreamWriter writer = null;

    try {
        pfd = resolver.openFileDescriptor(uri, "w");
        fos = new FileOutputStream(pfd.getFileDescriptor());
        writer = new OutputStreamWriter(fos);
        writer.write(text);
        writer.flush();
        result = true;
    } catch (FileNotFoundException e) {
        result = false;
    } catch (IOException e) {
        result = false;
    } finally {
        // 按逆序关闭资源
        try { if (writer != null) writer.close(); } catch (IOException e) {}
        try { if (fos != null) fos.close(); } catch (IOException e) {}
        try { if (pfd != null) pfd.close(); } catch (IOException e) {}
    }

    if (!result) openDialog();
}

2. 读取云存储文件在主线程执行网络操作

NextCloud读取文件需要发起网络请求,Android禁止主线程执行网络操作,因此抛出NetworkOnMainThreadException。需将读取逻辑移至子线程,示例如下:

// 异步读取方法,通过回调返回结果
static public void readTextDocumentAsync(Activity activity, String fileUri, OnReadCompleteListener listener) {
    new Thread(() -> {
        ContentResolver contentResolver = activity.getContentResolver();
        InputStream is = null;
        BufferedReader br = null;
        String result = null;

        try {
            is = contentResolver.openInputStream(Uri.parse(fileUri));
            if (is == null) {
                listener.onComplete(null);
                return;
            }
            InputStreamReader isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            StringBuilder textBuilder = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                textBuilder.append(line).append("\n");
            }
            result = textBuilder.toString();
        } catch (FileNotFoundException e) {
            result = null;
        } catch (IOException e) {
            result = null;
        } finally {
            try { if (br != null) br.close(); } catch (IOException e) {}
            try { if (is != null) is.close(); } catch (IOException e) {}
        }

        // 回到主线程回调结果
        activity.runOnUiThread(() -> listener.onComplete(result));
    }).start();
}

// 定义回调接口
interface OnReadCompleteListener {
    void onComplete(String content);
}

调用示例:

readTextDocumentAsync(this, fileUri, content -> {
    if (content != null) {
        // 处理读取到的内容
    } else {
        // 读取失败处理
    }
});

3. OneDrive未显示在文件选择器的解决建议

  • 确保OneDrive应用为最新版本
  • 清除OneDrive应用的缓存和数据后重启应用
  • 检查设备是否授予OneDrive必要的存储权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:58:13