Fetch: download.getTotal() not working in 3.1.3

Created on 29 Jun 2019  路  6Comments  路  Source: tonyofrancis/Fetch

Default fetch config.
Test url: https://www.apkmirror.com/wp-content/themes/APKMirror/download.php?id=730602
(Click download after load the page)

Fetch 3.1.3 => download.getTotal() retruns -1
Fetch 3.1.2 => download.getTotal() retruns 50.9MB which is correct

bug

All 6 comments

1619221544

Working perfectly

Please share some code to better understand

    implementation "androidx.tonyodev.fetch2:xfetch2:3.1.3"
    implementation "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:3.1.3"
    implementation 'com.squareup.okhttp3:okhttp:4.0.0'

  final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
                .setDownloadConcurrentLimit(3)
                .enableRetryOnNetworkGain(true)
                .enableAutoStart(false)
                .setAutoRetryMaxAttempts(10)
                .setProgressReportingInterval(1100)
.setHttpDownloader(new OkHttpDownloader(okHttpClient, ownloader.FileDownloaderType.PARALLEL)) 
                .build();

I fetch the file size (content-length) manually using okhttp (not using fetch.getcContentLength()) before start download. (For show to the user). Then start the download. I update the progress in recyclerview adapter (download.getTotal()).

You have to create Custom downloader class. Like this one

class CustomDownloader(okHttpClient: OkHttpClient) : OkHttpDownloader(okHttpClient) {

    private fun getResponseHeaders(okResponseHeaders: Headers): MutableMap<String, List<String>> {
        val headers = mutableMapOf<String, List<String>>()
        for (i in 0 until okResponseHeaders.size()) {
            val key = okResponseHeaders.name(i)
            val values = okResponseHeaders.values(key)
            headers[key] = values
        }
        return headers
    }

    private fun getRedirectedServerRequest(oldRequest: Downloader.ServerRequest, redirectUrl: String): Downloader.ServerRequest {
        return Downloader.ServerRequest(
                id = oldRequest.id,
                url = oldRequest.url,
                headers = oldRequest.headers,
                file = oldRequest.file,
                fileUri = oldRequest.fileUri,
                tag = oldRequest.tag,
                identifier = oldRequest.identifier,
                requestMethod = oldRequest.requestMethod,
                extras = oldRequest.extras,
                redirected = true,
                redirectUrl = redirectUrl,
                segment = oldRequest.segment)
    }

    fun getContentLengthFromHeader(headers: Map<String, List<String>>, defaultValue: Long): Long {
        var contentLength = defaultValue
        if (headers.containsKey("Content-Length")) {
            val size = headers["Content-Length"]?.firstOrNull()?.toLongOrNull()
            if (size != null && size > 0) {
                contentLength = size
                return contentLength
            }
        }
        if (headers.containsKey("content-length")) {
            val size = headers["content-length"]?.firstOrNull()?.toLongOrNull()
            if (size != null && size > 0) {
                contentLength = size
                return contentLength
            }
        }
        if (headers.containsKey("Content-Range")) {
            val value = headers["Content-Range"]?.firstOrNull()
            if (value != null) {
                val index = value.lastIndexOf("/")
                if (index != -1 && ((index + 1) < value.length)) {
                    val sizeText = value.substring(index + 1)
                    val size = sizeText.toLongOrNull()
                    if (size != null && size > 0) {
                        contentLength = size
                        return contentLength
                    }
                }
            }
        }
        if (headers.containsKey("content-range")) {
            val value = headers["content-range"]?.firstOrNull()
            if (value != null) {
                val index = value.lastIndexOf("/")
                if (index != -1 && ((index + 1) < value.length)) {
                    val sizeText = value.substring(index + 1)
                    val size = sizeText.toLongOrNull()
                    if (size != null && size > 0) {
                        contentLength = size
                        return contentLength
                    }
                }
            }
        }
        return contentLength
    }

     override fun execute(request: Downloader.ServerRequest, interruptMonitor: InterruptMonitor): Downloader.Response? {
         var okHttpRequest = onPreClientExecute(client, request)
         if (okHttpRequest.header("Referer") == null) {
             val referer = getRefererFromUrl(request.url)
             okHttpRequest = okHttpRequest.newBuilder()
                     .addHeader("Referer", referer)
                     .build()
         }
         var okHttpResponse = client.newCall(okHttpRequest).execute()
         var responseHeaders = getResponseHeaders(okHttpResponse.headers())
         var code = okHttpResponse.code()
         if ((code == HttpURLConnection.HTTP_MOVED_TEMP
                         || code == HttpURLConnection.HTTP_MOVED_PERM
                         || code == HttpURLConnection.HTTP_SEE_OTHER) && responseHeaders.containsKey("Location")) {
             okHttpRequest = onPreClientExecute(client, getRedirectedServerRequest(request,
                     responseHeaders["Location"]?.firstOrNull() ?: ""))
             if (okHttpRequest.header("Referer") == null) {
                 val referer = getRefererFromUrl(request.url)
                 okHttpRequest = okHttpRequest.newBuilder()
                         .addHeader("Referer", referer)
                         .build()
             }
             okHttpResponse = client.newCall(okHttpRequest).execute()
             responseHeaders = getResponseHeaders(okHttpResponse.headers())
             code = okHttpResponse.code()
         }
         val success = okHttpResponse.isSuccessful
         var contentLength = getContentLengthFromHeader(responseHeaders, -1)
         val byteStream: InputStream? = okHttpResponse.body()?.byteStream()
         val errorResponseString: String? = if (!success) {
             copyStreamToString(byteStream, false)
         } else {
             null
         }

         val hash = getContentHash(responseHeaders)

         if (contentLength < 1) {
             contentLength = responseHeaders["Content-Length"]?.firstOrNull()?.toLong() ?: -1L
         }

         val acceptsRanges = code == HttpURLConnection.HTTP_PARTIAL ||
                 responseHeaders["Accept-Ranges"]?.firstOrNull() == "bytes"

         onServerResponse(request, Downloader.Response(
                 code = code,
                 isSuccessful = success,
                 contentLength = contentLength,
                 byteStream = null,
                 request = request,
                 hash = hash,
                 responseHeaders = responseHeaders,
                 acceptsRanges = acceptsRanges,
                 errorResponse = errorResponseString))

         val response = Downloader.Response(
                 code = code,
                 isSuccessful = success,
                 contentLength = contentLength,
                 byteStream = byteStream,
                 request = request,
                 hash = hash,
                 responseHeaders = responseHeaders,
                 acceptsRanges = acceptsRanges,
                 errorResponse = errorResponseString)

         connections[response] = okHttpResponse
         return response
     }


     override fun getRequestFileDownloaderType(request: Downloader.ServerRequest, supportedFileDownloaderTypes: Set<Downloader.FileDownloaderType>): Downloader.FileDownloaderType {

         return Downloader.FileDownloaderType.PARALLEL
     }
 }

and then use it in fetch configuration

 final OkHttpClient client = new OkHttpClient.Builder().build();
        final Downloader customDownloader = new CustomDownloader(client);
        final FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)
                .enableRetryOnNetworkGain(true)
                .setDownloadConcurrentLimit(Integer.parseInt(sharedPreferences.getString("simultaneous_download","9")))
                .createDownloadFileOnEnqueue(true)
                .preAllocateFileOnCreation(true)
                .enableLogging(true)
                .setLogger(new FetchLogger())
                .setProgressReportingInterval(1000)
                .setPrioritySort(PrioritySort.ASC)
                .setHttpDownloader(customDownloader)
                .build();


        fetch = Fetch.Impl.getInstance(fetchConfiguration);

The issue you are facing will be fixed in next release as @tonyofrancis already push commit https://github.com/tonyofrancis/Fetch/commit/136bf515548e34557d9389c8e285cb70f1a89773
related to this issue.

Follow this issue for more info https://github.com/tonyofrancis/Fetch/issues/396

I use 3.1.2 for now, because custom download manager is not needed for my purpose. @maulik9898 Thank you very much for your timely clarification.

@jpvs0101 fixed in next release

@jpvs0101 This issue is now fixed in new Fetch version 3.0.10/Androidx 3.1.4.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jpvs0101 picture jpvs0101  路  5Comments

burhanaksendir picture burhanaksendir  路  4Comments

Mahan3340 picture Mahan3340  路  4Comments

DeevD picture DeevD  路  3Comments

emerging-coders picture emerging-coders  路  3Comments