6

最初我有以下代码:

尝试 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 调用之后。

我该如何解决?

4

2 回答 2

7

鉴于问题中的三个代码片段,我认为这应该可行:

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不会自动刷新...)。您可以使用's builder like pattern 使格式在关闭时刷新(感谢@PetrBodnár 提供此提示)。然而,这在上面的例子中可能没有什么不同。CSVPrinterCSVFormat.DEFAULTCSVFormatCSVFormat.DEFAULT.withAutoFlush(true)

如果您将 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.

于 2019-12-06T09:56:23.913 回答
0

以下是正确的用法:

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 not flushing 。或者将字节写入文本(使用 Writer io OutputStream),并期待 UTF-8,其多字节序列很脆弱,可能会引发异常。或者设置 HTTP 标头 Content-Length 错误,为 String.length()。

另一个原因:项目包含 null 或getEmail引发未检测到的异常。

可用的还有:

String s = output.toString(StandardCharsets.UTF_8);
于 2019-12-06T10:13:32.050 回答