Java 创建CSV并以字节[]的形式返回,以便在Spring控制器中下载

Java 创建CSV并以字节[]的形式返回,以便在Spring控制器中下载,java,spring,csv,spring-restcontroller,Java,Spring,Csv,Spring Restcontroller,摘要:ResponseEntity包含CSV的内容不会作为CSV返回 我正在为下载按钮编写一个控制器,该按钮将使用从另一个依赖项检索的数据生成CSV @PostMapping(基本路线+“/下载”) public ResponseEntity downloadCases(@RequestBody RequestType request,最终HttpServletResponse响应)引发IOException{ try(ByteArrayOutputStream outputStream=ne

摘要:
ResponseEntity
包含CSV的内容不会作为CSV返回


我正在为下载按钮编写一个控制器,该按钮将使用从另一个依赖项检索的数据生成CSV

@PostMapping(基本路线+“/下载”)
public ResponseEntity downloadCases(@RequestBody RequestType request,最终HttpServletResponse响应)引发IOException{
try(ByteArrayOutputStream outputStream=newbytearrayoutputstream();
OutputStreamWriter OutputStreamWriter=新的OutputStreamWriter(outputStream,StandardCharsets.UTF_8);
PrintWriter writer=新的PrintWriter(outputStreamWriter,true)){
字符串nextPageToken=null;
做{
ServiceResponse ServiceResponse=makeServiceCall(request.getParam());
//编写由逗号分隔的值组成的字符串
writer.println(servicesponse.getLine());
//最终无效
nextPageToken=serviceResponse.getToken();
}while(nextPageToken!=null);
返回ResponseEntity.ok()
.header(HttpHeaders.CONTENT_类型,“text/csv”)
.header(HttpHeaders.CONTENT\u处置,“附件;文件名=\“report.csv\”)
.body(outputStream.toByteArray());
}
作为测试,我还尝试将body设置为
.body(“Case ID,assignment”.getBytes(StandardCharsets.UTF_8))
。这应该相当于

在这两种情况下,我得到的响应看起来都像Base64。示例(缩短):
Q2FzZSBJRCxBc3NpZ25lZQ==

实际上它似乎不是Base64。使用
.body(Base64.getDecoder().decode(outputStream.toByteArray())
给我
java.lang.IllegalArgumentException:非法的Base64字符

如果我返回
void
并使用
IOUtils.write(outputStream.toByteArray(),response.getOutputStream());
将outputStream复制到response.getOutputStream(),则下载的文件是正确的(CSV)

但是,我希望避免直接调用response.getOutputStream()。如果之后出现任何异常,我会得到一个错误
getOutputStream(),该错误已经在
@ExceptionHandler
中为此响应调用了

编辑:Base64对响应进行解码会得到正确的值

System.out.println(新字符串(Base64.getDecoder().decode(“Q2FzZSBJRCxBc3NpZ25lZQ=”));
//案件编号,受让人

字节[]似乎在返回响应和客户端之间进行Base64编码(使用Postman和浏览器进行了尝试)。

不幸的是,我无法评论,因此我将作为答案发布,但这确实只是一些建议:

您是否尝试使用“应用程序/csv”而不是“文本/csv”

不久前,我不得不做一些非常类似的事情,通过查看您的代码,我没有发现任何问题

下面是我所做工作的一个例子:

@GetMapping("/" , produces="application/vnd.ms-excel")
public ResponseEntity<Object> myMethod() {
  String filename = "filename.xlsx";
  byte[] bytes = getBytes();
  HttpHeaders headers = new HttpHeaders();
  headers.set("Content-Type", "application/vnd.ms-excel;");
  headers.setContentDispositionFormData(filename, filename);
  return new ResponseEntity<Object>(bytes, headers, HttpStatus.OK);
}
@GetMapping(“/”,products=“application/vnd.ms excel”)
公众反应我的方法(){
字符串filename=“filename.xlsx”;
byte[]bytes=getBytes();
HttpHeaders=新的HttpHeaders();
headers.set(“内容类型”、“应用程序/vnd.ms excel;”);
headers.setContentDispositionFormData(文件名、文件名);
返回新的响应属性(字节、头、HttpStatus.OK);
}

TL;DR:不要以
字节[]
的形式给出数据,而是以
字符串的形式给出数据


使用时,Spring使用注册的

如果指定的内容类型为
应用程序/octet流
,主体类型为
字节[]
,Spring将使用,并按原样发送字节

如果指定的内容类型为
text/*
,例如指定的
text/csv
,主体类型为
String
,Spring将使用,它将根据需要对文本进行编码并发送。最好显式指定
字符集,例如使用内容类型
text/csv;字符集=UTF-8
,因此客户端知道服务器使用的字符集

由于您指定的内容类型为
text/csv
,但主体类型为
byte[]
,因此Spring正在使用另一种类型,可能是a,将
byte[]
编码为
String
作为Base-64

旁注:为了获得更好的性能,不要自动刷新输出的每一行。关闭自动刷新,完成后只需执行最后一次
flush()

长话短说,因为您需要文本,所以不要转换为
字节[]
。使用a将所有内容保留为文本:

@PostMapping(path = BASE_ROUTE + "/download", produces = "text/csv; charset=UTF-8")
public ResponseEntity<String> downloadCases(@RequestBody RequestType request) throws IOException {
    try (StringWriter stringWriter = new StringWriter();
            PrintWriter writer = new PrintWriter(stringWriter)) {
        String nextPageToken = null;
        do {
            ServiceResponse serviceResponse = makeServiceCall(request.getParam());
            // write a String of comma separated values
            writer.println(serviceResponse.getLine());
            // eventually null
            nextPageToken = serviceResponse.getToken();
        } while (nextPageToken != null);
        writer.flush();

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_TYPE, "text/csv; charset=UTF-8")
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"report.csv\"")
                .body(stringWriter.toString());
    }
}
@PostMapping(path=BASE\u ROUTE+“/download”,products=“text/csv;charset=UTF-8”)
public ResponseEntity downloadCases(@RequestBody RequestType request)引发IOException{
try(StringWriter-StringWriter=new-StringWriter();
PrintWriter=新的PrintWriter(stringWriter)){
字符串nextPageToken=null;
做{
ServiceResponse ServiceResponse=makeServiceCall(request.getParam());
//编写由逗号分隔的值组成的字符串
writer.println(servicesponse.getLine());
//最终无效
nextPageToken=serviceResponse.getToken();
}while(nextPageToken!=null);
writer.flush();
返回ResponseEntity.ok()
.header(HttpHeaders.CONTENT_类型,“text/csv;charset=UTF-8”)
.header(HttpHeaders.CONTENT\u处置,“附件;文件名=\“report.csv\”)
.body(stringWriter.toString());
}
}

由于您从未使用过
response
参数,因此该参数已被删除。

我尝试了此操作,但仍然从浏览器和postman获得Base64?您如何尝试在postman上保存文件?是否单击“发送和下载”()?如果将“products”属性添加到GetMapping中会发生什么?我编辑了我的