当前位置: 首页 > 知识库问答 >
问题:

创建CSV并作为字节[]返回,以便在Spring控制器中下载

燕和同
2023-03-14

总结:<代码>响应

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

    @PostMapping(BASE_ROUTE + "/download")
    public ResponseEntity<byte[]> downloadCases(@RequestBody RequestType request, final HttpServletResponse response) throws IOException {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
                PrintWriter writer = new PrintWriter(outputStreamWriter, true)) {
            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);

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_TYPE, "text/csv")
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"report.csv\"")
                    .body(outputStream.toByteArray());
        }

作为测试,我还尝试将主体设置为<代码>。正文(“案例ID,受让人”。getBytes(StandardCharsets.UTF\u 8))。这应等同于https://stackoverflow.com/a/34508533/10327093

在这两种情况下,我得到的回应都是Base64。示例(缩短):Q2FzZSBJRCxBc3NpZ25lZQ==

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

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

然而,我想避免呼叫响应。直接获取getOutputStream()。如果之后出现任何异常,我会得到一个错误,该错误已经在异常处理程序(ExceptionHandler)中为此响应调用

编辑:Base64解码响应给我正确的值

System.out.println(new String(Base64.getDecoder().decode("Q2FzZSBJRCxBc3NpZ25lZQ==")));
// Case ID,Assignee

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

共有2个答案

卢出野
2023-03-14

不幸的是,我不能发表评论,所以我将作为答案发布,但这确实只是一些建议:

您是否尝试使用“应用程序/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);
}
丁星火
2023-03-14

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

使用ResponseEntity时,Spring使用注册的HttpMessageConverter。

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

如果您指定的内容类型为文本,例如您正在指定的文本,正文类型为字符串,Spring将使用StringHttpMessageConverter对文本进行适当编码并发送。最好明确指定字符集,例如使用内容类型文本/csv;字符集=UTF-8,因此客户端知道服务器使用的字符集。

由于您指定的内容类型为文本/csv,但正文类型为字节[],Spring正在使用其他的HttpMessageConverter,可能是一个ObjecttoStringttpMessageConverter,带有一个将字节[]编码为字符串的转换服务,作为基64。

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

长话短说,既然您想要文本,就不要转换为字节[]。使用StringWriter将所有内容保留为文本:

@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());
    }
}

由于从未使用过响应参数,因此已将其删除。

 类似资料:
  • 让我们看看下面这个简单的测试控制器(与Spring 4.0.3一起使用): 理论上,两个控制器方法都应该返回有效的JSON。调用第一个controller方法确实会返回以下JSON数组: 但第二个controller方法返回不带引号的字符串,该字符串不是有效的JSON字符串: 为什么会这样?可以配置吗?是虫子吗?还是一个我不懂的特征?

  • 我一直在思考使用Spring MVC设计JSON API的最佳方法。众所周知,IO很昂贵,因此我不想让客户端进行多次API调用以获得他们需要的东西。然而,与此同时,我不一定想归还厨房水槽。 例如,我正在开发一个类似于IMDB的游戏API,但用于视频游戏。 如果我返回与游戏相关的所有内容,它将看起来像这样。 /api/game/1 然而,他们可能不需要所有这些信息,但他们可能需要这些信息。从I/O和

  • 我的问题实质上是对这个问题的跟进。 在上面,Spring会将“hello world”添加到响应体中。如何将字符串作为JSON响应返回?我知道我可以加引号,但这感觉更像是一个黑客。 请提供任何例子来帮助解释这一概念。 注意:我不想直接写到HTTP响应体,我想返回JSON格式的字符串(我正在使用带有RestyGWT的控制器,它要求响应是有效的JSON格式)。

  • 因此,我创建了我的对象数组,每个对象都被初始化为一个新字节[1024]: 然后,我遍历这个数组中的所有索引,执行如下操作: 在大多数情况下,一切都很好。但是,如果我希望每个字节数组都是可变长度的呢?也就是说,我希望字节数组的数组是“锯齿状”的。我将什么作为最后一个参数传递给NewObjectArray()作为初始值?我尝试传递0作为初始值,以防止在创建jobjectArray时进行初始化,然后分配

  • 问题内容: 我正在研究Java ExtJS应用程序,需要在其中创建和下载CSV文件。 单击按钮后,我希望将CSV文件下载到客户端计算机上。 在按钮侦听器上,我正在使用AJAX调用servlet。在那里,我正在创建一个CSV文件。 我不希望将CSV文件保存在服务器中。我希望使用下载选项 动态 创建文件。我希望将文件的内容创建为字符串,然后将其作为 文件 提供,然后在浏览器中以下载模式打开该 文件 (

  • 如果可以从Spring返回,我很好奇 这样做可以吗?我试过了,Spring返回的不是流的值。 我应该继续返回