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

如何在Kotlin+Arrow中安全关闭InputStream并返回Either错误?

解决方案

方案1:结合Kotlin标准库use与runCatching

Kotlin的use函数本身会保证InputStream在代码块执行完成后被关闭——无论代码块是正常返回还是抛出异常。你只需要在use的代码块内,用runCatching捕获所有可能的异常,并转换为Arrow的Either类型即可。

修改后的代码:

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import org.apache.commons.io.IOUtils
import java.io.InputStream
import java.nio.charset.Charset

fun pipeline(inputStream: InputStream): Either<Throwable, String> {
    return inputStream.use { stream ->
        runCatching {
            // 读取流内容
            val input = IOUtils.toString(stream, Charset.defaultCharset())
            
            // 解析JSON,失败则抛出异常交给runCatching统一处理
            val event = fromJson<S3EventNotification.S3Event>(input).fold(
                { throw it },
                { it }
            )
            
            // 获取业务数据,失败同样抛出异常
            val key = event.records.first().s3.s3object.key
            getZoomLocationData(key).fold(
                { throw it },
                { it.toString() }
            )
        }.fold(
            { it.left() }, // 所有异常转为Either.Left
            { it.right() } // 正常结果转为Either.Right
        )
    }
}

核心逻辑

  • use全权负责InputStream的安全关闭,无需手动编写finally块
  • runCatching捕获代码块内所有异常(包括主动抛出的解析/业务错误)
  • 通过fold将Either的Left值抛出,统一由runCatching捕获后转为最终的Either.Left

方案2:使用Arrow官方资源管理API(贴合函数式风格)

Arrow提供了Resource工具类,专门用于函数式场景下的资源生命周期管理,和Either、effect等API无缝集成,代码更简洁:

import arrow.core.Either
import arrow.core.continuations.effect
import arrow.resources.Resource
import org.apache.commons.io.IOUtils
import java.io.InputStream
import java.nio.charset.Charset

fun pipeline(inputStream: InputStream): Either<Throwable, String> = effect {
    // 将InputStream包装为Arrow资源,自动处理关闭逻辑
    val stream = Resource.fromCloseable { inputStream }.bind()
    
    val input = IOUtils.toString(stream, Charset.defaultCharset())
    // 用bind直接提取Either.Right的值,Left会自动转为异常终止流程
    val event = fromJson<S3EventNotification.S3Event>(input).bind()
    
    val key = event.records.first().s3.s3object.key
    val locationData = getZoomLocationData(key).bind()
    
    locationData.toString()
}.toEither()

核心逻辑

  • Resource.fromCloseable将InputStream转为Arrow资源,确保流程结束后自动关闭
  • effect块自动捕获所有异常,bind()简化了Either的处理:若为Left则终止流程并将异常传入最终的Either;若为Right则提取值继续执行
  • 最终调用toEither()将Effect类型转为Either<Throwable, String>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 20:25:25