Java Spring 返回使用 BOM 编码的 UTF-8 CSV 文件

Cod*_*ino 5 java spring byte-order-mark utf-8

显然,要让 excel 很好地打开 CSV 文件,它的开头应该有字节顺序标记。CSV 的下载是通过写入HttpServletResponse控制器中的输出流来实现的,因为数据是在请求期间生成的。当我尝试写入 BOM 字节时出现异常 - java.io.CharConversionException: Not an ISO 8859-1 character: [](即使我指定的编码是UTF-8)。


控制器的方法有问题

@RequestMapping("/monthly/list")
public List<MonthlyDetailsItem> queryDetailsItems(
        MonthlyDetailsItemQuery query,
        @RequestParam(value = "format", required = false) String format,
        @RequestParam(value = "attachment", required = false, defaultValue="false") Boolean attachment,
        HttpServletResponse response) throws Exception 
{   
    // load item list
    List<MonthlyDetailsItem> list = detailsSvc.queryMonthlyDetailsForList(query);
    // adjust format
    format = format != null ? format.toLowerCase() : "json";
    if (!Arrays.asList("json", "csv").contains(format)) format = "json";

    // modify common response headers
    response.setCharacterEncoding("UTF-8");
    if (attachment)
        response.setHeader("Content-Disposition", "attachment;filename=duomenys." + format);

    // build csv
    if ("csv".equals(format)) {
        response.setContentType("text/csv; charset=UTF-8");
        response.getOutputStream().print("\ufeff");
        response.getOutputStream().write(buildMonthlyDetailsItemCsv(list).getBytes("UTF-8"));
        return null;
    }

    return list;
}
Run Code Online (Sandbox Code Playgroud)

Bil*_*lor 5

我刚刚遇到,同样的问题。对我有用的解决方案是从响应对象获取输出流并按如下方式写入它

    // first create an array for the Byte Order Mark
    final byte[] bom = new byte[] { (byte) 239, (byte) 187, (byte) 191 }; 
    try (OutputStream os = response.getOutputStream()) {
        os.write(bom);

        final PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
        w.print(data);
        w.flush();
        w.close();
    } catch (IOException e) {
        // logit
    }
Run Code Online (Sandbox Code Playgroud)

因此在 OutputStreamWriter 上指定了 UTF-8。


作为对此的补充,我应该补充一点,同一个应用程序需要允许用户上传文件,这些文件可能有也可能没有 BOM。这可以通过使用类来处理org.apache.commons.io.input.BOMInputStream,然后使用它来构造一个org.apache.commons.csv.CSVParser. BOMInputStream 包括一个hasBOM()检测文件是否有 BOM的方法。我首先遇到的一个问题是该hasBOM()方法从底层流读取(显然!),因此处理这个问题的方法是首先标记流,然后在测试后如果它没有 BOM,则重置溪流。我为此使用的代码如下所示:

    // first create an array for the Byte Order Mark
    final byte[] bom = new byte[] { (byte) 239, (byte) 187, (byte) 191 }; 
    try (OutputStream os = response.getOutputStream()) {
        os.write(bom);

        final PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
        w.print(data);
        w.flush();
        w.close();
    } catch (IOException e) {
        // logit
    }
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助某人。