通过Spring rest模板下载大文件

时间:2016-05-20 16:37:46

标签: java spring resttemplate spring-rest

服务器代码:

@POST
@Path("reportDownload")
@Consumes(MediaType.APPLICATION_JSON)
public Response generateReport(QueryData queryData) {
     File file = new File("report.xlsx") // large file
     StreamingOutput stream = new FileStreamingOutput(file) ; 
        return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM)
            .header("filename" , file.getName())
            .build();
}

客户代码:

使用以下代码我可以下载文件到一定限度。大文件的内存堆错误。

final String uri = buildUri("/reportGenerate/reportDownload");

    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setReadTimeout(read_timeout);
    factory.setConnectTimeout(connection_timeout);

    RestTemplate restTemplate = new RestTemplate(factory);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    List<MediaType> mediaTypeList = new ArrayList<>();
    mediaTypeList.add(MediaType.APPLICATION_OCTET_STREAM);
    headers.setAccept(mediaTypeList);
    HttpEntity entity = new HttpEntity(queryData, headers);
    ResponseEntity<byte[]> data = restTemplate.exchange(uri, HttpMethod.POST, entity, byte[].class);
    HttpHeaders responseHeader = data.getHeaders();
    String fileName = (String) responseHeader.get("filename").get(0);
    String downloadFolder = ApplicationConfig.REPORT_DOWNLOAD_FOLDER.getValue();
    if (data.getStatusCode() == HttpStatus.OK) {
        FileOutputStream fos = null;
        File toFile = null;
        try {
            toFile = new File(downloadFolder + File.separator + fileName);
            fos = new FileOutputStream(toFile);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            IOUtils.write(data.getBody(), bos);
            bos.writeTo(fos);

        } catch (Exception e) {
            convertReportException(e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ex) {
                    convertReportException(ex);
                }
            }
            return toFile;
        }
    }

如何使用流来下载更大的文件。

1 个答案:

答案 0 :(得分:3)

以下是我使用ResponseExtractor的方式。基于此Spring Jira issue的提示。

RestTemplate restTemplate // = ...;

// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
        .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));

// Streams the response instead of loading it all in memory
ResponseExtractor<Void> responseExtractor = response -> {
    // Here I write the response to a file but do what you like
    Path path = Paths.get("some/path");
    Files.copy(response.getBody(), path);
    return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback, responseExtractor);

更新

以下是RestTemplate幕后postForObject和朋友(我的内联评论)所做的事情:

@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
        throws RestClientException {

    // From RequestCallback's javadoc:
    // Callback interface for code that operates on a ClientHttpRequest.
    // Allows to manipulate the request headers, and write to the request body. 
    //
    // Used internally by the RestTemplate, but also useful for application code.
    RequestCallback requestCallback = httpEntityCallback(request, responseType);

    // HttpMessageConverterExtractor checks the response type header and requested
    // responseType class to select the proper message converter to handle the response.
    // It also implements ResponseExtractor.
    HttpMessageConverterExtractor<T> responseExtractor =
            new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
    return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}

/**
 * Returns a request callback implementation that writes the given object to the
 * request stream.
 */
protected <T> RequestCallback httpEntityCallback(Object requestBody, Type responseType) {
    return new HttpEntityRequestCallback(requestBody, responseType);
}

注意:这基本上与https://stackoverflow.com/a/38664475/1030527的答案重复,但我无法将问题标记为重复,因为这一个或那个都没有得到答案。