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

Kotlin使用BouncyGPG PGP加密大文件后JSch SFTP传输异常求助

问题排查与修复方案

核心错误原因

  • BouncyGPG API使用错误:BouncyGPG.encryptToStream()....andWriteTo(output) 会返回一个经过加密包装的输出流,你必须向这个返回的流写入明文数据,加密逻辑才会生效。你现有代码直接向原始的output写入明文,完全跳过了BouncyGPG的加密处理流程,自然不会生成合法的加密文件。
  • 冗余的私钥导入:你配置了.andDoNotSign(),加密只需要公钥即可,导入私钥属于多余操作,还可能因为密码读取错误引发额外异常。
  • 资源泄漏风险:sendSFTP方法中的JSch会话、SFTP通道使用完没有主动关闭,长期运行会出现连接资源耗尽的问题。
  • 语法错误:sendFileSFTP方法参数MyFile后缺少右括号,encrypt方法的两个参数之间缺少逗号。

修复后的完整代码

BouncyGPG.registerProvider()

private val bufferSize: Int = 8 * 1024

fun sendFileSFTP(myFile: MyFile) {
    PipedInputStream().use { input ->
        GlobalScope.launch(Dispatchers.IO) { // 显式指定IO调度器,避免阻塞默认线程
            PipedOutputStream(input).use { output ->
                encrypt(myFile, output)
            }
        }
        sendSFTP(input, "mysftp-directory/${myFile.filename}")
        log.info("Finished Sending $myFile")
    }
}

fun encrypt(myFile: MyFile, output: OutputStream) {
    log.info("ENCRYPTING - $myFile")
    val pubkey = "/home/myhome/server.gpg"
    val password = "password"
    val keyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withPassword(password))
    // 只导入公钥即可,不需要私钥
    keyring.addPublicKey(File(pubkey).readText().toByteArray(charset("US-ASCII")))

    // 获取加密包装流,写入明文会自动加密后输出到原始output
    BouncyGPG.encryptToStream()
        .withConfig(keyring)
        .withStrongAlgorithms()
        .toRecipients("contact@myserver.com")
        .andDoNotSign()
        .binaryOutput()
        .andWriteTo(output).use { encryptOutputStream ->
            val filepath = Paths.get(myFile.path, myFile.filename)
            BufferedInputStream(FileInputStream(filepath.toString())).use { input ->
                input.copyTo(encryptOutputStream, bufferSize)
            }
        }

    log.info("FINISHED ENCRYPTING - $myFile")
}

fun sendSFTP(inputStream: InputStream, dest: String) {
    log.info("Sending $dest")
    var jsch = JSch()
    jsch.setKnownHosts(properties.knownHosts)

    var jschSession: Session? = null
    var channel: ChannelSftp? = null
    try {
        jschSession = jsch.getSession(properties.sFTPUsername, properties.sFTPHost, properties.sFTPPort)
        jschSession.setPassword(properties.sFTPPassword)
        jschSession.connect()
        channel = jschSession.openChannel("sftp") as ChannelSftp
        channel.connect()
        channel.put(inputStream, dest)
    } finally {
        // 主动释放资源
        channel?.disconnect()
        jschSession?.disconnect()
    }
}

额外优化建议

  • 避免使用GlobalScope,建议自行创建限定生命周期的协程作用域,避免任务泄漏。
  • 公钥、私钥、密码等敏感配置不要硬编码在代码中,建议放到配置文件或环境变量中读取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 01:36:08