如何安全地将 List 转换为 csv 字节数组?
How to convert List to the csv byte array safely?
最初我有以下代码:
尝试 1
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
externalHttpCall(output.toByteArray());
}
到这里我发现有时候字节数组写的不全
据我了解,这是因为在 externalHttpCall
次调用期间未刷新流。
为了修复它,我写了以下内容:
尝试 2
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
printer.flush();
log.info("Printer was flushed");
externalHttpCall(output.toByteArray());
}
它解决了问题,但在这里我迷失了一个想法,即仅在 externalHttpCall
之后关闭流是一个非常糟糕的主意。所以我想出了以下解决方案:
尝试 3
externalHttpCall(convertToByteArray(items);
public byte[] convertToByteArray(List<MyBean> items){
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
return output.toByteArray();
}
}
我预计刷新将在流关闭之前发生。但根据我的实验,它不起作用。看起来它的发生是因为刷新发生在流关闭之前但在 toByteArray 调用之后。
我该如何解决?
考虑到问题中的三个代码片段,我认为这应该有效:
externalHttpCall(convertToByteArray(items);
public byte[] convertToByteArray(List<MyBean> items){
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
printer.flush()
log.info("Printer was flushed");
return output.toByteArray();
}
}
根据 CSVFormat
,CSVPrinter
会在关闭时自动刷新(CSVFormat.DEFAULT
不会自动刷新...)。您可以使用 CSVFormat
的构建器模式使格式在关闭时与 CSVFormat.DEFAULT.withAutoFlush(true)
齐平(感谢@PetrBodnár 提供此提示)。然而,这可能对上面的示例没有影响。
如果您将 try-with-resource 转换为实际的调用顺序,您将得到如下内容:
var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)
printer.printRecord(EMAIL);
...
var result = output.toByteArray();
printer.close(); // might call flush
output.close();
return result;
由于关闭操作将在 finally 块中调用,因此它们将在 创建字节数组之后发生。如果需要刷新,您需要在调用 toByteArray
.
之前执行此操作
以下是正确的用法:
var output = new ByteArrayOutputStream();
try (var printer = new CSVPrinter(
new OutputStreamWriter(output, StandardCharsets.UTF_8), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
}
// Everything flushed and closed.
externalHttpCall(output.toByteArray());
此错误行为可能源于其他原因。
例如 externalHttpCall 不刷新。或者将字节写为文本(使用 Writer i.o.OutputStream),并期望 UTF-8,其 multi-byte 序列很脆弱,可能会引发异常。或者设置 HTTP header Content-Length 错误,如 String.length().
另一个原因:包含 null 或 getEmail
的项目引发未检测到的异常。
可用的还有:
String s = output.toString(StandardCharsets.UTF_8);
最初我有以下代码:
尝试 1
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
externalHttpCall(output.toByteArray());
}
到这里我发现有时候字节数组写的不全
据我了解,这是因为在 externalHttpCall
次调用期间未刷新流。
为了修复它,我写了以下内容:
尝试 2
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
printer.flush();
log.info("Printer was flushed");
externalHttpCall(output.toByteArray());
}
它解决了问题,但在这里我迷失了一个想法,即仅在 externalHttpCall
之后关闭流是一个非常糟糕的主意。所以我想出了以下解决方案:
尝试 3
externalHttpCall(convertToByteArray(items);
public byte[] convertToByteArray(List<MyBean> items){
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
return output.toByteArray();
}
}
我预计刷新将在流关闭之前发生。但根据我的实验,它不起作用。看起来它的发生是因为刷新发生在流关闭之前但在 toByteArray 调用之后。
我该如何解决?
考虑到问题中的三个代码片段,我认为这应该有效:
externalHttpCall(convertToByteArray(items);
public byte[] convertToByteArray(List<MyBean> items){
try (var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
printer.flush()
log.info("Printer was flushed");
return output.toByteArray();
}
}
根据 CSVFormat
,CSVPrinter
会在关闭时自动刷新(CSVFormat.DEFAULT
不会自动刷新...)。您可以使用 CSVFormat
的构建器模式使格式在关闭时与 CSVFormat.DEFAULT.withAutoFlush(true)
齐平(感谢@PetrBodnár 提供此提示)。然而,这可能对上面的示例没有影响。
如果您将 try-with-resource 转换为实际的调用顺序,您将得到如下内容:
var output = new ByteArrayOutputStream();
var printer = new CSVPrinter(new OutputStreamWriter(output), CSVFormat.DEFAULT)
printer.printRecord(EMAIL);
...
var result = output.toByteArray();
printer.close(); // might call flush
output.close();
return result;
由于关闭操作将在 finally 块中调用,因此它们将在 创建字节数组之后发生。如果需要刷新,您需要在调用 toByteArray
.
以下是正确的用法:
var output = new ByteArrayOutputStream();
try (var printer = new CSVPrinter(
new OutputStreamWriter(output, StandardCharsets.UTF_8), CSVFormat.DEFAULT)) {
printer.printRecord(EMAIL);
for (MyBean mb : items) {
printer.printRecord(mb.getEmail());
}
}
// Everything flushed and closed.
externalHttpCall(output.toByteArray());
此错误行为可能源于其他原因。
例如 externalHttpCall 不刷新。或者将字节写为文本(使用 Writer i.o.OutputStream),并期望 UTF-8,其 multi-byte 序列很脆弱,可能会引发异常。或者设置 HTTP header Content-Length 错误,如 String.length().
另一个原因:包含 null 或 getEmail
的项目引发未检测到的异常。
可用的还有:
String s = output.toString(StandardCharsets.UTF_8);