Android使用ACTION_CREATE_DOCUMENT保存URL响应PDF生成0字节问题
问题根因与修复方案
核心问题排查
- 错误配置HTTP GET请求参数
setDoOutput(true)用于需要向服务端提交请求体的请求(如POST、PUT),设置后会自动将请求方法改为POST,你原本的GET请求会被篡改,导致服务端返回异常/空响应,最终写入的内容为空,这是生成0字节文件的核心原因。
- 流操作缺少异常捕获与缓存刷新
当前代码没有任何异常捕获逻辑,请求、读写流过程中发生任何崩溃都会直接中断流程,导致写入不完整;且输出流写完后没有手动刷缓存,部分场景下缓冲区内容未写入文件就被关闭,也会生成0字节文件。
- 全局协程作用域不合理且无异常处理
GlobalScope没有绑定页面生命周期,页面销毁后任务仍在运行会导致内存泄漏,同时未配置异常处理器,协程运行时抛出异常会直接静默结束,你无法感知到错误发生。
- 权限遗漏风险
要确保你已经在
AndroidManifest.xml中配置了网络权限,否则请求远程PDF流时会直接抛出异常导致写入失败:<uses-permission android:name="android.permission.INTERNET" />
修复后的完整代码
1. ACTION_CREATE_DOCUMENT 启动逻辑(无改动)
fun saveFile() { val exportIntent = Intent(Intent.ACTION_CREATE_DOCUMENT) exportIntent.addCategory(Intent.CATEGORY_OPENABLE) exportIntent.type = "application/pdf" val filename = "test.pdf" exportIntent.putExtra(Intent.EXTRA_TITLE, filename) startForResult.launch(exportIntent) }
2. Activity 结果回调逻辑(替换协程作用域,绑定页面生命周期)
private val MEGABYTE = 1024 * 1024 val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> if (result.resultCode == Activity.RESULT_OK) { val uri = result.data?.data ?: return@registerForActivityResult // 改用绑定页面生命周期的lifecycleScope,页面销毁自动取消任务,避免内存泄漏 lifecycleScope.launch(Dispatchers.IO) { writeFile(uri) } } }
3. 修复后的文件写入方法
suspend fun writeFile(uri: Uri) { runCatching { // 用use方法自动关闭输出流,无需手动close requireContext().contentResolver.openOutputStream(uri)?.use { outputStream -> val url = URL("替换为你的PDF下载地址") val urlConnection = url.openConnection() as HttpURLConnection urlConnection.apply { requestMethod = "GET" // 删掉错误的setDoOutput(true),GET请求不需要提交请求体 connectTimeout = 15000 readTimeout = 15000 connect() } // 输入流也用use自动关闭 urlConnection.inputStream.use { inputStream -> val buffer = ByteArray(MEGABYTE) var readLen: Int while (inputStream.read(buffer).also { readLen = it } > 0) { outputStream.write(buffer, 0, readLen) } // 手动刷缓存,确保所有内容写入文件 outputStream.flush() } urlConnection.disconnect() } ?: throw IllegalArgumentException("无法获取对应URI的输出流") }.onSuccess { // 可切换到主线程做保存成功的提示 withContext(Dispatchers.Main) { Toast.makeText(requireContext(), "PDF保存成功", Toast.LENGTH_SHORT).show() } }.onFailure { e -> // 打印异常,方便排查问题 e.printStackTrace() withContext(Dispatchers.Main) { Toast.makeText(requireContext(), "保存失败:${e.message}", Toast.LENGTH_SHORT).show() } } }
内容的提问来源于stack exchange,提问作者WISHY
相关产品推荐
相关产品推荐

