WebDownloader.java

package com.olepoeschl.upme;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.function.Consumer;

public class WebDownloader implements UpdateDownloader, AutoCloseable {

    private final HttpClient client;

    public WebDownloader() {
        client = HttpClient.newBuilder()
            .followRedirects(HttpClient.Redirect.NORMAL)
            .build();
    }

    @Override
    public Path downloadUpdate(String downloadUrl, Consumer<Float> progressCallback) throws IOException {
        Path updateFilePath = Files.createTempFile("", "");

        URI uri = URI.create(downloadUrl);
        HttpRequest req = HttpRequest.newBuilder()
            .uri(uri)
            .timeout(Duration.ofMillis(10_000))
            .GET()
            .build();

        HttpResponse.BodyHandler<InputStream> handler = HttpResponse.BodyHandlers.ofInputStream();
        HttpResponse<InputStream> response;
        try {
            response = client.send(req, handler);
        } catch (InterruptedException e) {
            throw new RuntimeException("Could not download update: interrupted while sending http download request", e);
        }

        HttpHeaders headers = response.headers();
        long contentLength = headers.firstValueAsLong("Content-Length").orElse(-1L);

        try (
            InputStream is = response.body();
            FileOutputStream fos = new FileOutputStream(updateFilePath.toFile());
        ) {
            byte[] buffer = new byte[4096];
            long totalBytesDownloaded = 0L;
            int bytesDownloaded;

            while((bytesDownloaded = is.read(buffer)) != -1) {
                totalBytesDownloaded += bytesDownloaded;
                fos.write(buffer, 0, bytesDownloaded);
                if(progressCallback != null && contentLength > 0)
                    progressCallback.accept((float) totalBytesDownloaded / contentLength);
            }
        } catch (IOException e) {
            throw new IOException("Could not download update: I/O error while downloading file", e);
        }

        return updateFilePath;
    }

    @Override
    public void close() throws IOException {
        client.close();
    }
}