2014-10-28 6 views
6

У меня есть следующий ресурс Rest, который загружает файл из базы данных. Он отлично работает в браузере, однако, когда я пытаюсь сделать это с Java-клиента, как показано ниже, я получаю 406 (ошибка не принимается).Как программно использовать файл из Rest API с помощью Spring?

... 
@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

Могут ли эти файлы быть большими? Я бы предложил потоковое воспроизведение вместо загрузки всех байтов в массив и отправки его обратно –

+0

@TritonMan Размер файла составляет около 90 КБ, поэтому он не такой большой. – Sami

+1

Не уверен, что это полезно или нет, но моя команда только начала использовать модификацию для наших остальных вызовов. В основном вы определяете аннотированный интерфейс, тогда вы можете делать вызовы отдыха так же, как вы вызываете какой-либо другой метод. «Дооснащение» обрабатывает все детали в фоновом режиме: http://square.github.io/retrofit/ – StormeHawke

ответ

15

Код ответа 406 не принимается. Вам нужно указать заголовок запроса «Принять», который должен соответствовать полю «производит» вашего RequestMapping.

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 напрямую или http-компоненты apache.

+0

Я получаю эту ошибку, когда пытаюсь использовать File.class 'Не удалось извлечь ответ: нет подходящего HttpMessageConverter, найденного для типа ответа [class java.io.File] и типа содержимого [application/octet-stream]' что это этот параметр, представляющий в любом случае, не удалось найти информацию в документах. – Sami

+1

Это решение недействительно - ответ содержит весь массив байтов, что означает, что вы только что загрузили весь контент в память, и то, что вы делаете дальше, передает его в поток вывода файла. Это может вызвать OutOfMemoryError! – bmoc

+0

не помог ошибкам памяти – vels4j

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

0

Попробуйте использовать метод execute на RestTemplate с ResponseExtractor и прочитайте данные из потока с помощью экстрактора.

+0

У вас есть образец для этого с помощью ResponseExtractor? – alltej

+0

@alltej Нет, не знаю. –

1

Вы можете использовать InputStreamResource с ByteArrayInputStream.

@RestController 
public class SomeController { 
    public ResponseEntity<InputStreamResource> someResource() { 
     byte[] byteArr = ...; 
     return ResponseEntity.status(HttpStatus.OK).body(new InputStreamResource(new ByteArrayInputStream(byteArr))); 
    } 
} 
Смежные вопросы