You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus REST API如何在二进制流响应中导出UTF-8编码CSV?

解决Quarkus导出CSV中文乱码问题(无临时文件方案)

问题场景

基于Quarkus的REST API项目,通过ByteArrayInputStream生成二进制流导出CSV文件以避免创建临时文件。拉丁字符显示正常,但中文显示为问号;已确认数据库编码无问题,且JSON导出时中文显示正常。核心需求是在不生成实际文件的前提下,实现带指定编码的流式CSV导出。

核心原因

  1. 编码未显式指定:默认PrintWriter使用系统默认编码(非UTF-8),导致中文字符转码错误。
  2. 缺少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();
}

接口层关键调整

  1. 修改Produces类型:将APPLICATION_OCTET_STREAM改为text/csv;charset=UTF-8,明确告知客户端文件编码。
  2. 修正Content-Disposition格式:filename值用双引号包裹,避免空格或特殊字符导致解析错误。

验证方法

  • 下载CSV文件后,用记事本打开,中文应正常显示。
  • 用Notepad++查看文件编码,确认显示为「UTF-8(带BOM)」。

内容的提问来源于stack exchange,提问作者Furious Gamer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:55:18