Quarkus REST API如何在二进制流响应中导出UTF-8编码CSV?
解决Quarkus导出CSV中文乱码问题(无临时文件方案)
问题场景
基于Quarkus的REST API项目,通过ByteArrayInputStream生成二进制流导出CSV文件以避免创建临时文件。拉丁字符显示正常,但中文显示为问号;已确认数据库编码无问题,且JSON导出时中文显示正常。核心需求是在不生成实际文件的前提下,实现带指定编码的流式CSV导出。
核心原因
- 编码未显式指定:默认
PrintWriter使用系统默认编码(非UTF-8),导致中文字符转码错误。 - 缺少UTF-8 BOM:Windows平台的纯文本工具(如记事本)无法自动识别无BOM的UTF-8文件,会用系统默认编码(如GBK)解析,引发乱码。
解决方案
方案1:修复Apache Commons CSVPrinter实现
修改JobRunDataCsvStreamer类,确保输出UTF-8编码并添加BOM:
@ApplicationScoped public class JobRunDataCsvStreamer implements DataFormatStreamer<JobData> { @Override public ByteArrayInputStream streamDataToFormat(List<JobData> dataList) { try { ByteArrayOutputStream out = getCsvOutputStreamFor(dataList); return new ByteArrayInputStream(out.toByteArray()); } catch (IOException e) { throw new RuntimeException("Failed to convert job data: " + e.getMessage()); } } private ByteArrayOutputStream getCsvOutputStreamFor(List<JobData> dataList) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); // 写入UTF-8 BOM,帮助Windows工具识别编码 out.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); // 指定UTF-8编码的PrintWriter,避免系统编码干扰 CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(out, false, StandardCharsets.UTF_8), getHeaderFormat()); for (JobData jobData : dataList) { csvPrinter.printRecord(extractStringRowData(jobData)); } csvPrinter.flush(); csvPrinter.close(); return out; } private CSVFormat getHeaderFormat() { return CSVFormat.EXCEL .builder() .setDelimiter(";") .setHeader("ID", "Source term", "Target term") .build(); } private List<String> extractStringRowData(JobData jobData) { return Arrays.asList( String.valueOf(jobData.getId()), jobData.getSourceTerm(), jobData.getTargetTerm() ); } }
方案2:直接输出CSV(无CSVPrinter依赖)
若不需要CSVPrinter的格式化能力,直接写入时同样需添加BOM并指定UTF-8:
@GET @Path("/export/csv") @Produces("text/csv;charset=UTF-8") public Response getAllAsCsvExport() { List<JobData> jobData = jobRunDataRepository.getAll(); ByteArrayOutputStream out = new ByteArrayOutputStream(); try{ // 写入UTF-8 BOM out.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); // 指定UTF-8编码的PrintWriter PrintWriter pw = new PrintWriter(out, false, StandardCharsets.UTF_8); pw.println(String.format("%s;%s;%s", "ID", "Source term", "Target term")); for (JobData item : jobData) { pw.println(String.format("%s;%s;%s", String.valueOf(item.getId()), item.getSourceTerm(), item.getTargetTerm()) ); } pw.flush(); pw.close(); } catch (Exception e) { throw new RuntimeException("Failed to convert job data: " + e.getMessage()); } return Response.ok(out.toByteArray()) .header("content-disposition", "attachment;filename=\"job-data.csv\"") .build(); }
接口层关键调整
- 修改Produces类型:将
APPLICATION_OCTET_STREAM改为text/csv;charset=UTF-8,明确告知客户端文件编码。 - 修正Content-Disposition格式:
filename值用双引号包裹,避免空格或特殊字符导致解析错误。
验证方法
- 下载CSV文件后,用记事本打开,中文应正常显示。
- 用Notepad++查看文件编码,确认显示为「UTF-8(带BOM)」。
内容的提问来源于stack exchange,提问作者Furious Gamer
相关产品推荐
相关产品推荐

