Jackson ObjectWriter 只写入流中的第一个条目
Jackson ObjectWriter only writes first entry from stream
我想创建一个 Spring 引导控制器,它使用流中的数据创建 CSV 文件。我使用 Jackson CSV (jackson-dataformat-csv 2.12.1) 将数据流从数据库写入 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)
}
}
在您的代码中写入流时没有看到分配的列格式化程序。你能试试下面的方法吗:-
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(Dymmy.class);
schema = schema.withColumnSeparator('\t');
在 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
.
有比将 AUTO_CLOSE_TARGET
设置为 false 更好的替代方法,即使用 SequenceWritter。
val stream = StreamingResponseBody { outputStream ->
val mapper = CsvMapper()
val sequenceWriter = mapper.writer(mapper.schemaFor(Int::class.java).withHeader())
.writeValues(outputStream)
input.forEach { entity ->
sequenceWriter.write(entity)
}
}
我想创建一个 Spring 引导控制器,它使用流中的数据创建 CSV 文件。我使用 Jackson CSV (jackson-dataformat-csv 2.12.1) 将数据流从数据库写入 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)
}
}
在您的代码中写入流时没有看到分配的列格式化程序。你能试试下面的方法吗:-
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(Dymmy.class);
schema = schema.withColumnSeparator('\t');
在 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
.
有比将 AUTO_CLOSE_TARGET
设置为 false 更好的替代方法,即使用 SequenceWritter。
val stream = StreamingResponseBody { outputStream ->
val mapper = CsvMapper()
val sequenceWriter = mapper.writer(mapper.schemaFor(Int::class.java).withHeader())
.writeValues(outputStream)
input.forEach { entity ->
sequenceWriter.write(entity)
}
}