How to know the size of a file before downloading it?
Asked Answered
T

3

37

I have to download a file and I'm using this code, which is basically an AsyncTask that is meant to update a progress bar. But, since I don't know what's the file size I've been having to use the spinner progress bar. So, how can I get the file size before start downloading it so that I can use a normal progress bar?

Taveda answered 6/6, 2010 at 4:15 Comment(2)
Note that you should call urlConnection.connect() before attempting to access the Content-Length header. Not doing so might work, but not under all possible circumstances.Robotize
Didn't know that... it worked that way for me, but thanks for your advice. I'm going to edit the answer.Taveda
I
77

you can get a header called Content-Length form the HTTP Response object that you get, this will give you the length of the file. you should note though, that some servers don't return that information, and the only way to know the actual size is to read everything from the response.

Example:

URL url = new URL("http://server.com/file.mp3");
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
int file_size = urlConnection.getContentLength();
Interlaminate answered 6/6, 2010 at 7:12 Comment(2)
this will work on most cases, but if the file is too large, it won't work. see my post below for a solution that always works: https://mcmap.net/q/413765/-how-to-know-the-size-of-a-file-before-downloading-itAlleged
You should use getContentLengthLong() instead of getContentLength() for Android 7 and above. Below that prefer using Long.parseLong(urlConnection.getHeaderField("content-length"))Indulge
A
19

you can usually use getContentLength , but the best thing is it get the length by yourself (since it can bypass integer's max value) .

just parse the content-length header value by yourself . better parse it as long .

example:

final URL uri=new URL(...);
URLConnection ucon;
try
  {
  ucon=uri.openConnection();
  ucon.connect();
  final String contentLengthStr=ucon.getHeaderField("content-length");
  //...
  }
catch(final IOException e1)
  {
  }

do note that i can be any string , so use try catch , and if it's -1, empty , or null , it means that you can't know the size of the file since the server doesn't allow it.

EDIT: Here's a more updated code, using Kotlin:

@JvmStatic
@WorkerThread
fun getFileSizeOfUrl(url: String): Long {
    var urlConnection: URLConnection? = null
    try {
        val uri = URL(url)
        urlConnection = uri.openConnection()
        urlConnection!!.connect()
        if (VERSION.SDK_INT >= Build.VERSION_CODES.N)
            return urlConnection.contentLengthLong
        val contentLengthStr = urlConnection.getHeaderField("content-length")
        return if (contentLengthStr.isNullOrEmpty()) -1L else contentLengthStr.toLong()
    } catch (ignored: Exception) {
    } finally {
        if (urlConnection is HttpURLConnection)
            urlConnection.disconnect()
    }
    return -1L
}
Alleged answered 4/9, 2012 at 21:45 Comment(8)
Java's int primitive is a 32-bit signed integer; you probably won't surpass the max value with a Content-Type headerGlycolysis
actually you can, it's about 2GB .Alleged
so what to do at server side to make it allow..?Kathernkatheryn
@KalpeshLakhani I don't know about the server side. only the client side. The question here is about Android, which functions in this case as the client side. sorry. maybe those could help: #6918516 #14903904Alleged
ohk thanks a lot for reply..is there any other way to get size(in kb) of downloaded data from server ? method is post in my case. i m getting text data in xml format. thanks a lot for your valuable time buddy..:)Kathernkatheryn
@KalpeshLakhani I'm not sure what you are asking. Are you asking about the amount of data the app has downloaded so far of a file-url? if so, that's something that is a part of the "algorithm" to download a file - your loop includes reading from the inputStream, and you increase a counter of the bytes to know how many bytes were read so far. the "read" method of inputStream returns the number of bytes read: developer.android.com/reference/java/io/… . here's a sample usage : https://mcmap.net/q/149981/-android-reading-from-an-input-stream-efficientlyAlleged
Sorry for confusion..Actually i m calling one web service which will return me list of contacts in xml format..like..<contact><name>kalpesh</name><tech>Android</tech></contact><contact>..</contact>. so what i want is to found the size of this xml data which returns by Wservice. (i want size in kb). for example data size is 500kb etcKathernkatheryn
@KalpeshLakhani ok, what i've written is in bytes, so you just need to divide the result by 1024 in order to get it in KilloBytes (kb=KiloBits, KB=KiloBytes)Alleged
E
-1

In some environments (such as Manifest V3 browser extensions) it's impossible to access the response headers before the file is completely downloaded.

In situations like these, you might think of sending your fetch request with the 'head' method. However, the server from which you are fetching might not support it.

A good solution to overcome this challenge is to fetch 0 bytes of the file with the range header, if it's supported by the server, then use the Content-Range response header as a substitute for Content-Length.

Here's an example how to achieve this.

let response = await fetch(this.url, { headers: { 'Range': 'bytes=0-0' } });
let contentLength = parseInt(response.headers.get('Content-Range').split('/')[1], 10);

You can write a function that uses this workaround only as a fallback, as I experienced it's still slower than the fetch request with the head method.

Here's an example:

async function getFileSize(url) {
    try {
        // Attempt to get file size using HEAD method
        let headResponse = await fetch(url, { method: 'HEAD' });
        let contentLength = parseInt(headResponse.headers.get('Content-Length'), 10);

        return contentLength;
    } catch (headError) {
        // If HEAD request fails, use Range header as fallback
        try {
            let rangeResponse = await fetch(url, { headers: { 'Range': 'bytes=0-0' } });
            let contentRange = rangeResponse.headers.get('Content-Range');
            let contentLength = parseInt(contentRange.split('/')[1], 10);

            return contentLength;
        } catch (rangeError) {
            // Handle the error or return a default value
            console.error('Failed to retrieve file size');
            return null;
        }
    }
}
Expurgatory answered 11/2 at 4:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.