2014-10-28 9 views
6

Mam następujące zasoby Rest, które pobierają plik z bazy danych. Działa dobrze z przeglądarki, jednak gdy próbuję zrobić to z klienta Java, jak poniżej, otrzymuję 406 (błąd niedozwolony).Jak programowo spożywać plik z interfejsu API odpoczynku przy użyciu 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); 
} 
... 

Klient jest zainstalowany na tym samym serwerze z innego portu (komunikat daje poprawną nazwę):

... 
    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()); 
    ... 

Co ja tu brakuje?

+0

czy te pliki mogą być duże? Sugerowałbym przesyłanie strumieniowe zamiast ładowania wszystkich bajtów do tablicy i wysłania jej z powrotem. –

+0

@ TritonMan Rozmiar pliku wynosi około 90 KB, więc nie jest zbyt duży. – Sami

+1

Nie jestem pewien, czy to jest pomocne, czy nie, ale mój zespół właśnie zaczął korzystać z modernizacji naszych połączeń z resztą. Zasadniczo definiujesz interfejs z przypisami, wtedy możesz wykonywać połączenia spoczynkowe, tak jak wywołujesz inną metodę. Retrofit obsługuje wszystkie szczegóły w tle: http://square.github.io/retrofit/ – StormeHawke

Odpowiedz

15

Kod odpowiedzi to Nie zaakceptowano 406. Należy podać nagłówek żądania "Zaakceptuj", który musi być zgodny z polem "produ" w polu Zapytanie.

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); 

     } 

Małe ostrzeżenie: nie rób tego dla dużych plików. RestTemplate.exchange (...) zawsze ładuje całą odpowiedź do pamięci, abyś mógł uzyskać wyjątki OutOfMemory. Aby tego uniknąć, nie używaj Spring RestTemplate, ale raczej używaj standardu Java HttpUrlConnection bezpośrednio lub komponentów http apache.

+0

Otrzymuję ten błąd, gdy próbuję użyć File.class 'Nie można wyodrębnić odpowiedzi: nie znaleziono odpowiedniego HttpMessageConverter dla typu odpowiedzi [class java.io.File] i typu zawartości [application/oktet-stream]' co to jest parametr reprezentujący w każdym razie, nie mogłem znaleźć żadnych informacji w dokumentach. – Sami

+1

To rozwiązanie jest niepoprawne - odpowiedź zawiera tablicę całych bajtów, co oznacza, że ​​właśnie załadowano całą zawartość do pamięci, a następna jest przesyłana strumieniowo do strumienia wyjściowego pliku. Może to spowodować OutOfMemoryError! – bmoc

+0

nie pomógł w usunięciu błędu pamięci – vels4j

2

może spróbować to zmienić metodę odpoczynku jako takie:

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

także użyć czegoś takiego, aby pobrać plik, robisz to źle.

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

Musisz powiedzieć, gdzie zapisać plik, REST API nie zrobi tego za ciebie Nie sądzę.

+0

Nie rozumiem, dlaczego głosowanie w dół. To jest poprawna odpowiedź. FileUtils.copyURLToFile() rozwiązuje problem tylko w jednym wierszu kodu. – mhcuervo

0

Spróbuj użyć metody execute na RestTemplate z ResponseExtractor i odczytaj dane ze strumienia za pomocą ekstraktora.

+0

Czy masz do tego próbkę za pomocą narzędzia ResponseExtractor? – alltej

+0

@alltej Nie, nie mam. –

1

Możesz użyć InputStreamResource z ByteArrayInputStream.

@RestController 
public class SomeController { 
    public ResponseEntity<InputStreamResource> someResource() { 
     byte[] byteArr = ...; 
     return ResponseEntity.status(HttpStatus.OK).body(new InputStreamResource(new ByteArrayInputStream(byteArr))); 
    } 
} 
Powiązane problemy