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

Spring WebFlux下载图片并持久化至关系型数据库的正确方法

问题原因

你的代码返回空字节数组的核心原因是响应式操作的异步特性:调用subscribe()后,代码会立刻继续执行到return outputStream.toByteArray(),此时图片的下载和写入操作还没开始或完成,自然返回空数组。另外,DataBuffer的释放逻辑本身没问题,但异步执行导致流程没有等待IO完成。

解决方案

因为要求返回阻塞式的ByteArray,需要将响应式的WebFlux操作转为同步阻塞执行,同时确保所有数据写入完成后再返回结果。可以通过block()方法等待整个写入流程结束,同时正确处理DataBuffer的释放:

fun downloadImage(url: String): ByteArray? {
    val outputStream = ByteArrayOutputStream()
    
    return runCatching {
        // 发起请求获取DataBuffer流
        val dataBufferFlux = webClient.get().uri(url)
            .accept(MediaType.APPLICATION_OCTET_STREAM)
            .retrieve()
            .bodyToFlux(DataBuffer::class.java)

        // 将DataBuffer写入输出流,并等待所有操作完成
        DataBufferUtils.write(dataBufferFlux, outputStream)
            .doOnNext { DataBufferUtils.release(it) } // 释放已写入的DataBuffer
            .then() // 转换为Mono<Void>,表示整个流处理完成
            .block() // 阻塞等待所有写入操作结束

        // 返回结果,空数组时返回null
        outputStream.toByteArray().takeIf { it.isNotEmpty() }
    }.getOrNull() // 捕获异常并返回null,可根据需求调整异常处理逻辑
}
关键说明
  • 阻塞等待:使用then().block()确保整个DataBuffer流的写入操作全部完成后,才去读取输出流的内容。
  • DataBuffer释放:通过doOnNext { DataBufferUtils.release(it) }在每个DataBuffer写入完成后立即释放,避免内存泄漏;也可以替换为subscribe(DataBufferUtils.releaseConsumer()),但结合block()的话,用doOnNext更贴合同步阻塞的流程。
  • 异常处理:用runCatching包裹整个流程,避免网络请求或IO操作抛出未捕获异常,可根据业务需求调整异常处理逻辑(比如返回特定错误标识)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:12:38