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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:24:03