mirror of
https://github.com/SKCraft/Launcher.git
synced 2025-01-21 21:31:32 +01:00
Add support for continuing partial downloads
If the server offers byte-range requests & the request fails, we retry with a Range header to continue the download where it was interrupted.
This commit is contained in:
parent
4da80dfae0
commit
ae6fb109e5
@ -238,6 +238,7 @@ public class HttpDownloader implements Downloader {
|
||||
int trial = 0;
|
||||
boolean first = true;
|
||||
IOException lastException = null;
|
||||
HttpRequest.PartialDownloadInfo retryDetails = null;
|
||||
|
||||
do {
|
||||
for (URL url : urls) {
|
||||
@ -249,11 +250,16 @@ public class HttpDownloader implements Downloader {
|
||||
|
||||
try {
|
||||
request = HttpRequest.get(url);
|
||||
request.execute().expectResponseCode(200).saveContent(file);
|
||||
request.setResumeInfo(retryDetails).execute().expectResponseCode(200).saveContent(file);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
lastException = e;
|
||||
log.log(Level.WARNING, "Failed to download " + url, e);
|
||||
|
||||
Optional<HttpRequest.PartialDownloadInfo> byteRangeSupport = request.canRetryPartial();
|
||||
if (byteRangeSupport.isPresent()) {
|
||||
retryDetails = byteRangeSupport.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (++trial < tryCount);
|
||||
@ -277,5 +283,4 @@ public class HttpDownloader implements Downloader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ package com.skcraft.launcher.util;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.skcraft.concurrency.ProgressObservable;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
@ -17,10 +18,7 @@ import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static com.skcraft.launcher.LauncherUtils.checkInterrupted;
|
||||
import static org.apache.commons.io.IOUtils.closeQuietly;
|
||||
@ -46,6 +44,7 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
private InputStream inputStream;
|
||||
private int redirectCount;
|
||||
|
||||
private PartialDownloadInfo resumeInfo = null;
|
||||
private long contentLength = -1;
|
||||
private long readBytes = 0;
|
||||
|
||||
@ -143,6 +142,10 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
conn.setDoInput(true);
|
||||
}
|
||||
|
||||
if (resumeInfo != null) {
|
||||
conn.setRequestProperty("Range", String.format("bytes=%d-", resumeInfo.currentLength));
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
conn.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@ -198,6 +201,11 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
}
|
||||
}
|
||||
|
||||
if (resumeInfo != null && responseCode == 206) {
|
||||
// Allow 206 Partial Content for resumed requests
|
||||
return this;
|
||||
}
|
||||
|
||||
close();
|
||||
throw new IOException("Did not get expected response code, got " + responseCode + " for " + url);
|
||||
}
|
||||
@ -284,7 +292,7 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
BufferedOutputStream bos = null;
|
||||
|
||||
try {
|
||||
fos = new FileOutputStream(file);
|
||||
fos = new FileOutputStream(file, resumeInfo != null);
|
||||
bos = new BufferedOutputStream(fos);
|
||||
|
||||
saveContent(bos);
|
||||
@ -340,6 +348,24 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<PartialDownloadInfo> canRetryPartial() {
|
||||
if (conn.getHeaderField("Accept-Ranges").equals("bytes")) {
|
||||
return Optional.of(new PartialDownloadInfo(contentLength, readBytes));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public HttpRequest setResumeInfo(PartialDownloadInfo info) {
|
||||
this.resumeInfo = info;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isResumedRequest() {
|
||||
return resumeInfo != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getProgress() {
|
||||
if (contentLength >= 0) {
|
||||
@ -594,4 +620,10 @@ public class HttpRequest implements Closeable, ProgressObservable {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class PartialDownloadInfo {
|
||||
private final long expectedLength;
|
||||
private final long currentLength;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user