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
相关产品推荐
相关产品推荐

