2014-10-28 17 views
6

我有以下Rest資源從DB下載文件。它在瀏覽器中工作正常,但是,當我嘗試從Java客戶端執行操作時,我得到了406(不接受錯誤)。如何以編程方式使用Spring使用Rest API中的文件?

... 
@RequestMapping(value="/download/{name}", method=RequestMethod.GET, 
     produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) 
public @ResponseBody HttpEntity<byte[]> downloadActivityJar(@PathVariable String name) throws IOException 
{ 
    logger.info("downloading : " + name + " ... "); 
    byte[] file = IOUtils.toByteArray(artifactRepository.downloadJar(name)); 
    HttpHeaders header = new HttpHeaders(); 
    header.set("Content-Disposition", "attachment; filename="+ name + ".jar"); 
    header.setContentLength(file.length); 

    return new HttpEntity<byte[]>(file, header); 
} 
... 

客戶端部署有不同的端口在同一臺服務器(信息提供了正確的名稱):

... 
    RestTemplate restTemplate = new RestTemplate(); 
    String url = "http://localhost:8080/activities/download/" + message.getActivity().getName(); 
    File jar = restTemplate.getForObject(url, File.class); 
    logger.info("File size: " + jar.length() + " Name: " + jar.getName()); 
    ... 

缺少什麼我在這裏?

+0

這些文件可能很大嗎?我建議將它流式傳輸,而不是將所有字節加載到數組中,然後將其發送回 – 2014-10-28 16:21:25

+0

@TritonMan文件的大小約爲90Kb,所以不會太大。 – Sami 2014-10-28 16:28:32

+1

不知道這是否有幫助,但我的團隊剛開始使用改造我們的休息電話。基本上你定義了一個帶註釋的接口,然後你可以像其他方法一樣調用其他的方法。 Retrofit處理後臺中的所有細節: http://square.github.io/retrofit/ – StormeHawke 2014-10-31 12:24:09

回答

15

響應代碼是406 Not Accepted。 您需要指定一個'Accept'請求標頭,它必須與RequestMapping的'produce'字段匹配。

RestTemplate restTemplate = new RestTemplate(); 
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());  
HttpHeaders headers = new HttpHeaders(); 
headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); 
HttpEntity<String> entity = new HttpEntity<String>(headers); 

ResponseEntity<byte[]> response = restTemplate.exchange(URI, HttpMethod.GET, entity, byte[].class, "1"); 

if(response.getStatusCode().equals(HttpStatus.OK)) 
     {  
       FileOutputStream output = new FileOutputStream(new File("filename.jar")); 
       IOUtils.write(response.getBody(), output); 

     } 

一個小警告:不要對大文件這樣做。 RestTemplate.exchange(...)總是將整個響應加載到內存中,因此您可能會遇到OutOfMemory異常。爲避免這種情況,請不要使用Spring RestTemplate,而應直接使用Java標準HttpUrlConnection或apache http-components。

+0

當我嘗試使用File.class時出現此錯誤'無法提取響應:找不到適用於響應類型[類java.io.File]和內容類型[application/octet-stream]的HttpMessageConverter'此參數表示什麼?無論如何,在文檔中找不到任何信息。 – Sami 2014-10-30 12:47:09

+1

此解決方案無效 - 響應包含整個字節數組,這意味着您已將所有內容加載到內存中,並且您接下來要做的是將其傳輸到文件輸出流。這可能會導致OutOfMemoryError! – bmoc 2016-05-03 08:47:24

+0

沒有幫助內存不足錯誤 – vels4j 2016-05-20 15:28:19

2

也許試試這個,改變你的休息方法,例如:

public javax.ws.rs.core.Response downloadActivityJar(@PathVariable String name) throws IOException { 
    byte[] file = IOUtils.toByteArray(artifactRepository.downloadJar(name)); 
    return Response.status(200).entity(file).header("Content-Disposition", "attachment; filename=\"" + name + ".jar\"").build(); 
} 

此外,使用這樣的下載文件,你做錯了。

org.apache.commons.io.FileUtils.copyURLToFile(new URL("http://localhost:8080/activities/download/" + message.getActivity().getName()), new File("locationOfFile.jar")); 

你需要告訴它在哪裏保存文件時,REST API不會爲你做的,我不認爲。

+0

不明白爲什麼投票。這是一個有效的答案。 FileUtils.copyURLToFile()僅在一行代碼中解決了這個問題。 – mhcuervo 2016-09-22 23:31:58

0

嘗試在RestTemplate上使用ResponseExtractor的execute方法,並使用提取器從流中讀取數據。

+0

你有使用ResponseExtractor的樣品嗎? – alltej 2017-11-07 22:31:02

+0

@alltej不,我不知道。 – 2017-11-08 07:22:35

1

您可以使用InputStreamResource和ByteArrayInputStream。

@RestController 
public class SomeController { 
    public ResponseEntity<InputStreamResource> someResource() { 
     byte[] byteArr = ...; 
     return ResponseEntity.status(HttpStatus.OK).body(new InputStreamResource(new ByteArrayInputStream(byteArr))); 
    } 
} 
相關問題