【问题标题】:How to download large file into byte array如何将大文件下载到字节数组中
【发布时间】:2018-06-29 06:07:20
【问题描述】:

我正在尝试使用 spring Rest Template 下载大文件:

    private Result download(Long id) throws Exception {
    Result result = new Result();
    try {
        RestTemplate restTemplate = restTemplateFactory.getRestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.set(AUTHORIZATION, authorizationBL.getAccessToken().getAccessToken());
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(null, headers);
        Map<String, Long> valuesMap = new HashMap<String, Long>(1);
        valuesMap.put(ID, id);
        ResponseEntity<byte[]> response = restTemplate.exchange(downloadURL, HttpMethod.GET, request, byte[].class);
        byte[] stream = response.getBody();
        result.setData(stream);
        result.setStatus(Result.Status.SUCCESS);
    } catch (Exception e) {
        throw e;
    }
    return result;
}

当文件大小很大时,可以说超过 100 MB,那么我遇到了问题:

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236) ~[na:1.8.0_60]
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:191) ~[na:1.8.0_60]
at org.springframework.http.converter.ByteArrayHttpMessageConverter.readInternal(ByteArrayHttpMessageConverter.java:59) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.http.converter.ByteArrayHttpMessageConverter.readInternal(ByteArrayHttpMessageConverter.java:38) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:924) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:908) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:662) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:538) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]

我将堆大小增加到 10g,但也没有帮助。

【问题讨论】:

  • 我需要将大文件转换为字节数组以便进一步处理
  • 进一步处理是什么意思?
  • 您确实应该考虑另一种方式来满足您的业务需求。在字节数组中存储如此大量的数据并不是一个好主意。如果您提供最初的目标,有人可能会帮助您找到实现此目标的可靠方法。即您可以将其保存到文件中以供进一步处理。

标签: java spring spring-boot resttemplate


【解决方案1】:

不要将文件下载到字节数组中,而是将其保存到磁盘。然后,您可以从程序中需要的任何其他位置读取它。例如,将您的方法更改为

/**
 * returns a File corresponding to the tmp file download for this resource
 */
private File download(Long id) throws IOException {
    File tmp = null;
    try {
        RestTemplate restTemplate = restTemplateFactory.getRestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.set(AUTHORIZATION, authorizationBL.getAccessToken().getAccessToken());
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(null, headers);
        ResponseEntity<byte[]> response = restTemplate.exchange(downloadURL, HttpMethod.GET, request, byte[].class);

        if (response.getStatusCode() == HttpStatus.OK) {
            tmp = Files.createTempFile("yourprefix", "yoursuffix");
            Files.write(tmp.toPath(), response.getBody());
        } else {
            // TODO handle this case
        }
    } catch (Exception e) {
        throw new IOException("An exception occurred while downloading", e);
    }
    return tmp;
}

一些观察:

  1. valuesMap 未在任何地方使用(也已删除)
  2. result 也被删除了

【讨论】:

  • 不鼓励抛出 Exception。最好将其更改为 IOException
  • 另外return tmp; 无效,tmp 仅在 if 块内可用
【解决方案2】:

我能够使用流解决此问题,而不是将文件存储在 byte[] 中并使用IOUtils.copyLarge(inputStream, outputStream)

【讨论】:

  • 你能在这里详细发布你的解决方案吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 2013-12-06
  • 1970-01-01
  • 2019-07-13
  • 1970-01-01
相关资源
最近更新 更多