我必须下载一个文件,并且我正在使用此代码,它基本上是一个
AsyncTask
,用于更新进度栏。但是,由于我不知道文件大小是多少,所以我不得不使用旋转进度条。那么,如何在开始下载之前获取文件大小,以便我可以使用正常的进度条?
您可以从您获得的 HTTP 响应对象中获取名为
Content-Length
的标头,这将为您提供文件的长度。
但您应该注意,某些服务器不会返回该信息,了解实际大小的唯一方法是从响应中读取所有内容。
示例:
URL url = new URL("http://server.com/file.mp3");
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
int file_size = urlConnection.getContentLength();
您通常可以使用 getContentLength ,但最好的是它自己获取长度(因为它可以绕过整数的最大值)。
自己解析content-length header值即可。最好解析它越长越好。
示例:
final URL uri=new URL(...);
URLConnection ucon;
try
{
ucon=uri.openConnection();
ucon.connect();
final String contentLengthStr=ucon.getHeaderField("content-length");
//...
}
catch(final IOException e1)
{
}
请注意, i 可以是任何 string ,因此请使用 try catch ,如果它是 -1、empty 或 null ,则意味着您无法知道文件的大小,因为服务器不允许。
编辑:这是使用 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
}
在某些环境(例如 Manifest V3 浏览器扩展)中,在文件完全下载之前无法访问响应标头。
在这种情况下,您可能会考虑使用“head”方法发送获取请求。但是,您从中获取数据的服务器可能不支持它。
克服这一挑战的一个好解决方案是使用范围标头获取文件的 0 字节,如果服务器支持,则使用 Content-Range 响应标头作为 Content-Length 的替代品。
这是如何实现此目的的示例。
let response = await fetch(this.url, { headers: { 'Range': 'bytes=0-0' } });
let contentLength = parseInt(response.headers.get('Content-Range').split('/')[1], 10);
您可以编写一个仅使用此解决方法作为后备的函数,因为我发现它仍然比使用 head 方法的获取请求慢。
这是一个例子:
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;
}
}
}