Search code examples
javascriptjavaexcelcsvbyte-order-mark

Send CSV file encoded in UTF-8 with BOM in Java


I'm writing a CSV file with some accents, encoded in UTF-8. The problem is that when I try to open the file with Excel, the content is still messed up. I have to open my file with Sublime Text and save it in "UTF-8 with BOM" in order to have a correct file (saving the file as simple UTF-8 does not work). I read many SO questions and tried to apply the solutions, but none of them worked. I can't figure out if this is a backend issue when I write my file, or a frontend issue when I download it.

Spring controller

@Transactional
@RequestMapping(method = RequestMethod.GET, path = "/exportStreets")
public void exportStreets(@RequestParam(value = "city", required = false) Long cityId, HttpServletResponse response) throws IOException {
    // Do some stuff ...

    response.setContentType("text/csv");
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename=\"export_streets.csv\"");
    this.exportService.exportStreetsToCsv(toExport, response.getWriter());
}

Export service

public void exportStreetsToCsv(Set<Street> streets, PrintWriter writer) throws IOException {
    writer.print('\ufeff'); // Write BOM

    CSVFormat csvFormat = CSVFormat.EXCEL.withQuoteMode(QuoteMode.ALL).withDelimiter(';');
    CSVPrinter csvPrinter = new CSVPrinter(writer, csvFormat);

    // Print records

    csvPrinter.flush();
    csvPrinter.close();
}

Frontend

const blobFile = new Blob([response.data], { type: 'text/csv' });
this.FileSaver.saveAs(blobFile, 'test.csv');

I managed to make it work by adding BOM before saving the file, but it's an ugly fix that I'd like to avoid. Plus, for some reason, with that fix the first line of my CSV file has double quotes. Here's the fix:

const blobFile = new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), response.data], { type: 'text/csv;charset=utf-8' });

Solution

  • I didn't do much to fix my issue, and I'm still not sure what was wrong. I only had to change the PrintWriter to a Writer, and add the charset in my javascript code.

    Backend service

    public void exportStreetsToCsv(Set<Street> streets, Writer writer) throws IOException {
        writer.write('\uFEFF'); // Write BOM
        // ...
    

    Frontend download

    const blobFile = new Blob([response.data], { type: 'text/csv;charset=utf-8' });
    this.FileSaver.saveAs(blobFile, 'test.csv');