Skip to content

Commit 5965ee2

Browse files
Jakob Stolzeaoles
authored andcommitted
fix: rewrite downloadFile for consistent download progress
HttpURLConnection behaves strange when downloading big files. For a consistent download progress calculation the method was rewritten to using more robust Apache HttpClient 5.
1 parent 91a9e6b commit 5965ee2

File tree

2 files changed

+70
-23
lines changed

2 files changed

+70
-23
lines changed

core/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@
166166
<artifactId>log4j-core</artifactId>
167167
<version>${log4j.version}</version>
168168
</dependency>
169+
<dependency>
170+
<groupId>org.apache.httpcomponents.core5</groupId>
171+
<artifactId>httpcore5</artifactId>
172+
<version>5.3.1</version>
173+
</dependency>
174+
<dependency>
175+
<groupId>org.apache.httpcomponents.client5</groupId>
176+
<artifactId>httpclient5</artifactId>
177+
<version>5.4.1</version>
178+
</dependency>
169179

170180
</dependencies>
171181

core/src/main/java/com/graphhopper/util/Downloader.java

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
import java.util.zip.GZIPInputStream;
2424
import java.util.zip.Inflater;
2525
import java.util.zip.InflaterInputStream;
26+
import org.apache.hc.client5.http.classic.methods.HttpGet;
27+
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
28+
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
29+
import org.apache.hc.client5.http.impl.classic.HttpClients;
30+
import org.apache.hc.core5.http.HttpEntity;
31+
import org.apache.hc.core5.http.io.entity.EntityUtils;
32+
import org.apache.hc.core5.http.Header;
2633

2734
/**
2835
* @author Peter Karich
@@ -116,30 +123,60 @@ public HttpURLConnection createConnection(String urlStr) throws IOException {
116123
}
117124

118125
public void downloadFile(String url, String toFile) throws IOException {
119-
HttpURLConnection conn = createConnection(url);
120-
InputStream iStream = fetch(conn, false);
121-
int size = 8 * 1024;
122-
int contentLength = conn.getContentLength();
123-
BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(toFile), size);
124-
InputStream in = new BufferedInputStream(iStream, size);
125-
try {
126-
byte[] buffer = new byte[size];
127-
int numRead;
128-
int i = 0;
129-
while ((numRead = in.read(buffer)) != -1) {
130-
writer.write(buffer, 0, numRead);
131-
i += numRead;
132-
if (progressListener != null)
133-
progressListener.update((int) (100 * i / (double) contentLength));
134-
}
135-
136-
if (progressListener != null) {
137-
progressListener.update(100);
126+
final int bufferSize = 4096; // 4kB buffer
127+
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
128+
HttpGet httpGet = new HttpGet(url);
129+
130+
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
131+
int statusCode = response.getCode();
132+
if (statusCode < 200 || statusCode >= 300) {
133+
throw new IOException("Server returned HTTP response code: " + statusCode + " for URL: " + url);
134+
}
135+
136+
HttpEntity entity = response.getEntity();
137+
if (entity == null) {
138+
throw new IOException("No entity found in response for URL: " + url);
139+
}
140+
141+
long contentLength = entity.getContentLength(); // Länge von der Entity holen
142+
if (contentLength <= 0) {
143+
Header lengthHeader = response.getFirstHeader("Content-Length");
144+
if(lengthHeader != null) {
145+
try {
146+
contentLength = Long.parseLong(lengthHeader.getValue());
147+
} catch (NumberFormatException e) {
148+
contentLength = -1;
149+
}
150+
}
151+
}
152+
153+
try (InputStream inputStream = entity.getContent(); // InputStream ist AutoCloseable
154+
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(toFile))) {
155+
156+
byte[] buffer = new byte[bufferSize];
157+
int numRead;
158+
long totalBytesRead = 0;
159+
int lastPercentage = -1;
160+
161+
while ((numRead = inputStream.read(buffer)) != -1) {
162+
outputStream.write(buffer, 0, numRead);
163+
totalBytesRead += numRead;
164+
165+
if (progressListener != null && contentLength > 0) {
166+
int currentPercentage = (int) (100 * totalBytesRead / (double) contentLength);
167+
if (currentPercentage > lastPercentage) {
168+
progressListener.update(currentPercentage);
169+
lastPercentage = currentPercentage;
170+
}
171+
}
172+
}
173+
if (progressListener != null && contentLength > 0 && lastPercentage < 100) {
174+
progressListener.update(100);
175+
}
176+
} finally {
177+
EntityUtils.consume(entity);
178+
}
138179
}
139-
} finally {
140-
Helper.close(iStream);
141-
Helper.close(writer);
142-
Helper.close(in);
143180
}
144181
}
145182

0 commit comments

Comments
 (0)