Transfer plików pomiędzy serwerem a klientem z wykorzystaniem Spring boot REST

Odpowiedz Nowy wątek
2019-06-28 17:21
0

Cześć, potrzebuję napisać usługę API REST na serwerze, która będzie wywoływana przez aplikacje kliencką w celu pobrania pliku. Pliki jakie będą przesyłane to ok 1-5GB. Dodatkowo usługa powinna być odporna na słabe łącze i zerwanie połączenia(powinno wznawiać przesyłanie) . Napisałem taki serwis, który "bierze" plik i przesyła go dalej do klienta i w przypadku małych plików nie ma żadnego problemu, został przesłany. Problem pojawił się już przy pliku ok 100mb, bo nie dostałem odpowiedzi, a żądanie mieliło i mieliło...

Poniżej kod wysyłania pliku:

FileInputStream inputStream = new FileInputStream(file2download);
        response.setContentType(URLConnection.guessContentTypeFromName(file2download.getName()));
        response.setContentLength((int) file2download.length());
        String headerValue = String.format("attachment; filename=\"%s\"", file2download.getName());
        response.setHeader("Content-Disposition", headerValue);
        OutputStream outStream = response.getOutputStream();
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead = -1; // czytamy w pętli po fragmencie, który następnie przepisujemy do strumienia wyjściowego
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }
        inputStream.close();
        outStream.close();

Nie udało mi się znaleźć żadnych ciekawych bibliotek, które pomogły by mi rozwiązać mój problem, czy koś ma jakiś pomysł i może mi pomóc? :)

Pozostało 580 znaków

2019-07-09 13:31
0

Poszedłem teraz w kierunku streamowania za Twoją radą (używam spring boota):

FileInputStream inputStream = new FileInputStream(file21download);
        StreamingResponseBody stream = outputStream -> {
            int numberOfBytesToWrite;
            byte[] data = new byte[1024];
            while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
                System.out.println("Writing some bytes..");
                outputStream.write(data, 0, numberOfBytesToWrite);
                outputStream.flush();
            }
        };

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file21download.getName())                
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(stream);

Testując, wywołuje api w przeglądarce pobierając plik ok 400mb, ale po przestremowaniu ok 60-100mb wysypuje mi się serwer:

2019-07-09 11:36:37.705  INFO 14476 --- [io-11015-exec-2] o.a.catalina.connector.CoyoteAdapter     : Encountered a non-recycled response and recycled it forcedly.

org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException: null
    at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:501)
    at org.apache.coyote.http11.Http11Processor.recycle(Http11Processor.java:1708)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.release(AbstractProtocol.java:988)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:877)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

Próbowałem też użyć:

 FileInputStream inputStream = new FileInputStream(file21download);
        StreamingResponseBody stream = outputStream -> {
            Files.copy(file21download.toPath(), outputStream);           
        };

Ale efekt ten sam.
O to chodziło Ci ze streamowaniem? Dlaczego dostaje taki błąd?
Mam ustawiony parametr w application.properties spring.mvc.async.request-timeout = 3600000

edytowany 1x, ostatnio: turo90, 2019-07-09 13:42

Pozostało 580 znaków

2019-07-09 15:31
0

Czemu nie zrobisz po prostu:

ResponseEntity.ok().body(new InputStreamResource(new FileInputStream("someFile")))

Masz problem? Pisz na forum, nie do mnie. Nie masz problemów? Kup komputer...

Pozostało 580 znaków

2019-07-09 15:38
0

Robiłem prawie tak, z tymże jako osobny obiekt

InputStreamResource inputStreamResource = new InputStreamResource(new FileInputStream(file21download));
return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file21download.getName())               
                .contentLength(file21download.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(stream);

I o ile pliki były kilka mb, to w odpowiedzi je dostawałem, jak próbowałem przesłać plik już powyżej 100mb, to nie dostawałem odpowiedzi żadnej. A stosując powyższy kod, nie potrafię zażądać w requeście tylko fragmentu pliku, tak żeby wywoływać usługę tyle razy, aż pobiorę bezpiecznie cały plik..

Pozostało 580 znaków

2019-07-09 16:30
0

A stosując powyższy kod, nie potrafię zażądać w requeście tylko fragmentu pliku, tak żeby wywoływać usługę tyle razy, aż pobiorę bezpiecznie cały plik..

Tzn? Jak wysyłasz content range header to nie działa? Jesteś pewien?

edit: dobra sprawdziłem i to generalnie działa ale dla resource które są nawigowalne, więc jak dasz FileSystemResource to pyknie.


Masz problem? Pisz na forum, nie do mnie. Nie masz problemów? Kup komputer...
edytowany 1x, ostatnio: Shalom, 2019-07-09 17:17
@turo90 żebyś nie przegapił mojego edita ;) return ResponseEntity.of(new FileSystemResource("jakisplik")); działa ok - Shalom 2019-07-09 17:22

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 0, botów: 1

Robot: Googlebot