Jackson ObjectWriter 仅从流中写入第一个条目

Posted

技术标签:

【中文标题】Jackson ObjectWriter 仅从流中写入第一个条目【英文标题】:Jackson ObjectWriter only writes first entry from stream 【发布时间】:2021-06-01 02:45:22 【问题描述】:

我想创建一个 Spring Boot 控制器,它使用流中的数据创建一个 CSV 文件。我使用 Jackson CSV (jackson-dataformat-csv 2.12.1) 将数据流从 DB 写入 StreamingResponseBody。

为简单起见,我将数据库中的实际数据替换为包含1, 2, 3 的列表。我想要一个如下所示的 CSV 文件:

1
2
3

但它只包含第一个条目 (1)。有人可以帮我找出问题吗?

请注意,我不想在服务器的某处创建文件,我想将内容直接流式传输给用户。

我的代码如下所示:

import com.fasterxml.jackson.dataformat.csv.CsvMapper
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody
import javax.servlet.http.HttpServletResponse

@RestController
@RequestMapping(value = ["/test"], produces = [MediaType.TEXT_html_VALUE])
class TestController 

    @GetMapping("/download", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
    fun download(response: HttpServletResponse): ResponseEntity<StreamingResponseBody>? 
        response.setHeader("Content-Disposition", "attachment;filename=download.csv")
        response.status = HttpServletResponse.SC_OK

        val mapper = CsvMapper()
        val schema = mapper.schemaFor(Int::class.java)
        val writer = mapper.writer(schema)
        val input = listOf(1, 2, 3).stream()

        val stream = StreamingResponseBody  outputStream ->
            input.forEach  entity ->
                writer.writeValue(outputStream, entity)
            
        

        return ResponseEntity
            .ok()
            .header("Content-Disposition", "attachment;filename=download.csv")
            .body(stream)
    

【问题讨论】:

看起来writer 内部关闭了outputStream 并且只写入了第一个元素。我想你需要配置 writer 不这样做。 【参考方案1】:

在 Andriy 的评论的帮助下,我找到了原因和解决方案。 Jackson 在完成写入后关闭流,请参阅:ObjectMapper._writeValueAndClose。

要更改此行为,您必须将 JsonGenerator.Feature.AUTO_CLOSE_TARGET 设置为 false,如下所示:

val jsonFactory = CsvFactory().configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false)
val mapper = ObjectMapper(jsonFactory)
val writer = mapper.writer(CsvMapper().schemaFor(Int::class.java))

注意CsvGenerator 没有AUTO_CLOSE_TARGET 选项,但使用JsonGenerator 设置也适用于CsvFactory

【讨论】:

【参考方案2】:

在您的代码中写入流时没有看到指定的列格式化程序。您可以尝试以下方法吗:-

    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = mapper.schemaFor(Dymmy.class);
    schema = schema.withColumnSeparator('\t');

【讨论】:

不幸的是,这并不能解决问题。

以上是关于Jackson ObjectWriter 仅从流中写入第一个条目的主要内容,如果未能解决你的问题,请参考以下文章

连续从流中读取?

从流中播放波形文件

如何使用 Java 8 lambda 从流中获取一系列项目?

WPF中的MediaElement从流中播放视频?

从流中以新方式排序数据

MySQL 从流中读取失败