B4A Library [B4X] RangeDownloader - resumable downloads

1628754839844.png


RangeDownloader uses http range feature to download the file in chunks. It will resume the download from the previous point, even if the app was previously killed.
It first sends a HEAD request to test whether this feature is supported.
Note that you need to delete the target file if you want to restart the download.

Supported by: B4A, B4i and B4J.

Usage example:
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Private Downloader As RangeDownloader
    Private AnotherProgressBar1 As AnotherProgressBar 'XUI Views
    Private url As String = "https://sabnzbd.org/tests/internetspeed/20MB.bin"
    Private Label1 As B4XView
End Sub

Public Sub Initialize
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    xui.SetDataFolder("Download large file")
    Downloader.Initialize
    Dim tracker As RangeDownloadTracker = Downloader.CreateTracker
    Track(tracker)
    Wait For (Downloader.Download(xui.DefaultFolder, "test2.zip", url, tracker)) Complete (Success As Boolean)
    Log("Complete, success = " & Success)
    AnotherProgressBar1.Visible = False
End Sub

Private Sub Track (Tracker As RangeDownloadTracker)
    Do While Tracker.Completed = False
        Sleep(100)
        Label1.Text = $"$1.2{Tracker.CurrentLength / 1024 / 1024}MB / $1.2{Tracker.TotalLength / 1024 / 1024}MB"$
        AnotherProgressBar1.Value = Tracker.CurrentLength / Tracker.TotalLength * 100
    Loop
End Sub
 

Attachments

  • RangeDownloader.b4xlib
    1.3 KB · Views: 103
Last edited:

Mahares

Expert
Licensed User
Does anybody have a url link to download a very large file from a public site to test this new class which appears to be awesome. Maybe 20 to 30 MB or so.
 

LucaMs

Expert
Licensed User
Coincidentally, "we" were just looking for and discussing a topic related to this.

Is it possible, with appropriate modifications, to use multiple connections to the same file to download, each of which downloads a part of the file and then reunites them in a single file?

The aim is obviously to be able to speed up the download. Let's assume that the server dedicates N Kb to each connection and you are able to launch the download with 5 connections, which do not download the whole file, but each 1/5 of the file.
 

Gabino A. de la Gala

Active Member
Licensed User
Hum... It seems to happend when you try another file, even if you delete the test.zip file.

I had to modify the class to get download the file.
The head.Response.ContentLength always returns 0.

B4X:
Public Sub Download (Dir As String, FileName As String, URL As String, Tracker As RangeDownloadTracker) As ResumableSub
	Dim head As HttpJob
	head.Initialize("", Me)
	head.Head(URL)
	Wait For (head) JobDone (head As HttpJob)
	head.Release 'the actual content is not needed
	If head.Success Then
		' This doesn't work. contentLength returns 0
		'Tracker.TotalLength = head.Response.ContentLength
		' Workaround to get it works
		'Dim m As Map = head.Response.GetHeaders
		'Dim x As String = m.Get("content-length")
		'Tracker.TotalLength = x.SubString2(1, x.Length-1)
		Tracker.TotalLength = GetCaseInsensitiveHeaderValue(head, "content-length", "").As(Long)
		Log(head.Response.GetHeaders.As(JSON).ToString)
'		log("->" & GetCaseInsensitiveHeaderValue(head, "Accept-Ranges", "").As(String))
		If GetCaseInsensitiveHeaderValue(head, "Accept-Ranges", "").As(String) <> "bytes" Then
			Log("accept ranges not supported")
			Tracker.Completed = True
			Return False
		End If
	Else
		Tracker.Completed = True
		Return False
	End If
	Log("Total length: " & NumberFormat(Tracker.TotalLength, 0, 0))
	If File.Exists(Dir, FileName) Then
		Tracker.CurrentLength = File.Size(Dir, FileName)
	End If
	Dim out As OutputStream = File.OpenOutput(Dir, FileName, True) 'append = true
	Do While Tracker.CurrentLength < Tracker.TotalLength
		Dim j As HttpJob
		j.Initialize("", Me)
		j.Download(URL)
		Dim range As String = $"bytes=${Tracker.CurrentLength}-${(Min(Tracker.TotalLength, Tracker.CurrentLength + 300 * 1024) - 1).As(Int)}"$
		Log(range)
		j.GetRequest.SetHeader("Range", range)
		Wait For (j) JobDone (j As HttpJob)
		Dim good As Boolean = j.Success
		If j.Success Then
			Wait For (File.Copy2Async(j.GetInputStream, out)) Complete (Success As Boolean)
			#if B4A or B4J
			out.Flush
			#end if
			good = good And Success
			If Success Then
				Tracker.CurrentLength = File.Size(Dir, FileName)
			End If
		End If
		j.Release
		If good = False Then
			Tracker.Completed = True
			Return False
		End If
	Loop
	out.Close
	Tracker.Completed = True
	Return True
End Sub
 
Last edited:

behnam_tr

Active Member
Licensed User
testing sample in first post :

B4X:
Call B4XPages.GetManager.LogEvents = True to enable logging B4XPages events.
Total length: 0
Complete, success = true

i check this with many different links but result is same
 

Erel

Administrator
Staff member
Licensed User
If you are asking about the progress tracking then it is possible with: https://www.b4x.com/android/forum/t...ts-file-uploads-with-progress.130181/#content (this example creates a multipart file upload).

Resumable uploads are not possible as there is no such feature in http protocol. If you are uploading files to your server then it shouldn't be too difficult to implement a similar protocol and upload the file in chunks.
 
Top