简体   繁体   English

如何安全地将 List 转换为 csv 字节数组?

[英]How to convert List to the csv byte array safely?

Initially I had the following code:最初我有以下代码:

Attempt 1尝试 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());
}

Here I found out that sometimes the byte array is not written fully.在这里我发现有时字节数组没有完全写入。

I understand that it is because of the fact that stream is not flushed during externalHttpCall invocations.我知道这是因为在externalHttpCall调用期间没有刷新流。

To fix it I wrote the following:为了修复它,我写了以下内容:

Attempt 2尝试 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());
}

It solved the problem, but here I was lost in a thought that it is really bad idea to close stream only after externalHttpCall .它解决了这个问题,但在这里我迷失了一个想法,即仅在externalHttpCall之后关闭流是非常糟糕的主意。 So I came up with the following solution:所以我想出了以下解决方案:

Attempt 3尝试 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();    
  }
}

I expected that flush will happen before stream close.我预计在流关闭之前会发生刷新。 But based on my experiments it doesn't work.但根据我的实验,它不起作用。 Looks like it happens because of flush happens before stream close but after toByteArray invocation.看起来这是因为刷新发生在流关闭之前但在 toByteArray 调用之后。

How could I fix it?我怎么能修好呢?

Given the three code snippets in the question I'd assume that this should work:鉴于问题中的三个代码片段,我认为这应该有效:

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();
  }
}

Depending on the CSVFormat the CSVPrinter is flushed automatically on close ( CSVFormat.DEFAULT will not be flushed automatically...).根据CSVFormatCSVPrinter在关闭时自动刷新( CSVFormat.DEFAULT不会自动刷新...)。 You can use CSVFormat 's builder like pattern to make the format flush on close with CSVFormat.DEFAULT.withAutoFlush(true) (thanks to @PetrBodnár for this hint).您可以使用CSVFormat的 builder like pattern 使格式在关闭时与CSVFormat.DEFAULT.withAutoFlush(true)齐平(感谢@PetrBodnár 提供此提示)。 This will however probably make no difference in the above example.然而,这在上面的例子中可能没有区别。

If you translate the try-with-resource to the actual call order you will get something like this:如果您将 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;

As the close operations will be called in the finally-block, they will take place after creation of the byte array.由于关闭操作将在 finally 块中调用,因此它们将创建字节数组后进行。 If flush is needed, you will need to do it prior to calling toByteArray .如果需要刷新,则需要在调用toByteArray之前执行此操作。

The following is a correct usage:以下是正确的用法:

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());

This error behavior might stem from something else.此错误行为可能源于其他原因。

For instance the externalHttpCall not flushing .例如 externalHttpCall 不刷新。 Or writing the bytes as text (using a Writer io OutputStream), and expecting UTF-8, whose multi-byte sequences are brittle, maybe raising an exception.或者将字节写为文本(使用 Writer io OutputStream),并期待 UTF-8,其多字节序列很脆弱,可能会引发异常。 Or setting the HTTP header Content-Length wrong, as String.length().或者将 HTTP 标头 Content-Length 设置错误,如 String.length()。

An other cause: items containing a null or getEmail throwing an exception that is not detected.另一个原因:包含 null 或getEmail抛出未检测到的异常。

Available is also:可用的还有:

String s = output.toString(StandardCharsets.UTF_8);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM