From 00ef0f6e771f4de0230800444f50bb3a00981f0d Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 27 Apr 2014 19:03:35 +0200 Subject: Resume failed downloads. closes #168 --- .../service/download/DownloadService.java | 11 +++++- .../service/download/HttpDownloader.java | 45 ++++++++++++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index 855f33a3f..2d82d8631 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -30,7 +30,9 @@ import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException; import de.danoeh.antennapod.util.ChapterUtils; import de.danoeh.antennapod.util.DownloadError; import de.danoeh.antennapod.util.InvalidFeedException; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -160,7 +162,14 @@ public class DownloadService extends Service { if (!status.isCancelled()) { if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) { postAuthenticationNotification(downloader.getDownloadRequest()); - } else { + } else if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR + && Integer.valueOf(status.getReasonDetailed()) == HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE) { + + Log.d(TAG, "Requested invalid range, restarting download from the beginning"); + FileUtils.deleteQuietly(new File(downloader.getDownloadRequest().getDestination())); + DownloadRequester.getInstance().download(DownloadService.this, downloader.getDownloadRequest()); + } + else { Log.e(TAG, "Download failed"); saveDownloadStatus(status); handleFailedDownload(status, downloader.getDownloadRequest()); diff --git a/src/de/danoeh/antennapod/service/download/HttpDownloader.java b/src/de/danoeh/antennapod/service/download/HttpDownloader.java index ca6655002..7ae96dc07 100644 --- a/src/de/danoeh/antennapod/service/download/HttpDownloader.java +++ b/src/de/danoeh/antennapod/service/download/HttpDownloader.java @@ -14,10 +14,12 @@ import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.message.BasicHeader; import java.io.*; import java.net.HttpURLConnection; @@ -36,7 +38,9 @@ public class HttpDownloader extends Downloader { @Override protected void download() { File destination = new File(request.getDestination()); - if (destination.exists()) { + final boolean fileExists = destination.exists(); + + if (request.isDeleteOnFailure() && fileExists) { Log.w(TAG, "File already exists"); if (request.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE) { onFail(DownloadError.ERROR_FILE_EXISTS, null); @@ -48,10 +52,12 @@ public class HttpDownloader extends Downloader { } HttpClient httpClient = AntennapodHttpClient.getHttpClient(); - BufferedOutputStream out = null; + RandomAccessFile out = null; InputStream connection = null; try { HttpGet httpGet = new HttpGet(URIUtil.getURIFromRequestUrl(request.getSource())); + + // add authentication information String userInfo = httpGet.getURI().getUserInfo(); if (userInfo != null) { String[] parts = userInfo.split(":"); @@ -64,6 +70,15 @@ public class HttpDownloader extends Downloader { httpGet.addHeader(BasicScheme.authenticate(new UsernamePasswordCredentials(request.getUsername(), request.getPassword()), "UTF-8", false)); } + + // add range header if necessary + if (fileExists) { + request.setSoFar(destination.length()); + httpGet.addHeader(new BasicHeader("Range", + "bytes=" + request.getSoFar() + "-")); + if (BuildConfig.DEBUG) Log.d(TAG, "Adding range header: " + request.getSoFar()); + } + HttpResponse response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); int responseCode = response.getStatusLine().getStatusCode(); @@ -75,7 +90,7 @@ public class HttpDownloader extends Downloader { if (BuildConfig.DEBUG) Log.d(TAG, "Response code is " + responseCode); - if (responseCode != HttpURLConnection.HTTP_OK || httpEntity == null) { + if (responseCode / 100 != 2 || httpEntity == null) { final DownloadError error; final String details; if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) { @@ -96,14 +111,31 @@ public class HttpDownloader extends Downloader { connection = new BufferedInputStream(AndroidHttpClient .getUngzippedContent(httpEntity)); - out = new BufferedOutputStream(new FileOutputStream( - destination)); + + Header[] contentRangeHeaders = (fileExists) ? response.getHeaders("Content-Range") : null; + + if (fileExists && responseCode == HttpStatus.SC_PARTIAL_CONTENT + && contentRangeHeaders != null && contentRangeHeaders.length > 0) { + String start = contentRangeHeaders[0].getValue().substring("bytes ".length(), + contentRangeHeaders[0].getValue().indexOf("-")); + request.setSoFar(Long.valueOf(start)); + Log.d(TAG, "Starting download at position " + request.getSoFar()); + + out = new RandomAccessFile(destination, "rw"); + out.seek(request.getSoFar()); + } else { + destination.delete(); + destination.createNewFile(); + out = new RandomAccessFile(destination, "rw"); + } + + byte[] buffer = new byte[BUFFER_SIZE]; int count = 0; request.setStatusMsg(R.string.download_running); if (BuildConfig.DEBUG) Log.d(TAG, "Getting size of download"); - request.setSize(httpEntity.getContentLength()); + request.setSize(httpEntity.getContentLength() + request.getSoFar()); if (BuildConfig.DEBUG) Log.d(TAG, "Size is " + request.getSize()); if (request.getSize() < 0) { @@ -133,7 +165,6 @@ public class HttpDownloader extends Downloader { if (cancelled) { onCancelled(); } else { - out.flush(); // check if size specified in the response header is the same as the size of the // written file. This check cannot be made if compression was used if (!isGzip && request.getSize() != DownloadStatus.SIZE_UNKNOWN && -- cgit v1.2.3