如何在Scoped Storage中保存PDF?Android存储适配问题求助
解决Retrofit下载PDF无法写入内部存储的问题
我懂你现在的困扰:切换到Retrofit下载PDF后,明明请求响应成功了,可就是没法把数据流写入内部存储的文件里。结合你的代码,我整理了几个关键的排查点和修复方案:
1. 先确保目标目录已创建
你的代码直接创建了文件对象,但如果DESTINATION_DIRECTORY对应的文件夹不存在,写入操作会静默失败。在创建文件之前,一定要先检查并创建父目录:
val destDir = File(context.filesDir, DESTINATION_DIRECTORY) if (!destDir.exists()) { destDir.mkdirs() // 递归创建所有层级的目录 } file = File(destDir, "$FILE_NAME$FILE_EXTENSION")
2. 给数据流拷贝加上异常处理
当前的copyInputStreamToFile没有捕获IO异常,一旦出现读写错误你完全不知道问题出在哪。修改函数,加上异常捕获和详细日志:
private fun File.copyInputStreamToFile(inputStream: InputStream) { this.outputStream().use { fileOut -> try { Log.i("new","开始写入文件:${this.path}") inputStream.copyTo(fileOut, bufferSize = 8192) // 指定缓冲区大小提升写入效率 fileOut.flush() // 强制把缓存数据写入磁盘 Log.i("new","文件写入完成") } catch (e: IOException) { Log.e("new", "写入文件失败", e) throw e // 可以选择抛出异常让上层处理,也可以根据业务需求做降级处理 } } }
3. 妥善处理Retrofit的响应数据流
Retrofit返回的数据流需要确保在整个拷贝过程中保持打开状态,建议把inputStream也放进use块里,避免资源泄漏:
response.body()?.byteStream()?.use { inputStream -> file!!.copyInputStreamToFile(inputStream) }
4. 验证文件写入后的状态
在写入完成后,添加日志检查文件大小是否合理:
Log.i("new", "写入后文件大小:${file?.length() ?: 0} bytes")
如果文件大小为0,说明数据流根本没写入;如果远小于预期,可能是数据流读取不完整。
后续PDFRenderer渲染的注意事项
内部存储的文件是应用私有,PDFRenderer可以直接读取,但要注意:
- 确保文件完整(可以对比服务器端文件大小)
- 打开文件时用
ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) - 渲染完成后要关闭
PDFRenderer和ParcelFileDescriptor,避免资源泄漏
修改后的完整readAndDownloadFile函数示例:
fun readAndDownloadFile(context: Context) { readQuraanInterface?.downloadFile() Coroutines.io { val destDir = File(context.filesDir, DESTINATION_DIRECTORY) if (!destDir.exists()) { destDir.mkdirs() } file = File(destDir, "$FILE_NAME$FILE_EXTENSION") if (file?.exists() == true) { Log.i("new", "文件已存在,直接渲染") renderPDF() showPdf(mPageIndex, Direction.None) } else { Log.i("new","开始下载PDF") val response = readQuraanRepository.downloadPdf() if (response.isSuccessful) { Log.i("new","下载响应成功,准备写入文件:${file!!.path}") response.body()?.byteStream()?.use { inputStream -> file!!.copyInputStreamToFile(inputStream) } Log.i("new","文件写入完成,大小:${file?.length() ?: 0} bytes") renderPDF() showPdf(mPageIndex, Direction.None) } else { Log.i("new","下载失败,响应码:${response.code()}") Coroutines.main { response.errorBody()?.string()?.let { readQuraanInterface?.downloadFailed(it) } } } } } }
你可以先按这些步骤排查,重点看日志里的错误信息,应该能很快定位到写入失败的原因。
内容的提问来源于stack exchange,提问作者Neat
相关产品推荐
相关产品推荐

