B4J Question [B4X] RangeDownloader - How to manage user/password and errors/success?

Elric

Well-Known Member
Licensed User
Hello everybody!

I'm wondering if there is a way to manage user/password and errors/success using RangeDownloader lib like in jOkHttpUtils2
B4X:
job.Username = User1
job.Password = 123456
job.ErrorMessage
job.Success

Thanks!
 
Solution
Thanks.

Do you mean
B4X:
Sub Class_Globals
    Type RangeDownloadTracker (CurrentLength As Long, TotalLength As Long, Completed As Boolean, Cancel As Boolean, UserName As String, Password As String, _
        hErrorMessage As String, hStatusCode As Int, jErrorMessage As String, jStatusCode As Int)
End Sub

Public Sub Download2 (Dir As String, FileName As String, URL As String, Tracker As RangeDownloadTracker) As ResumableSub
    Dim head As HttpJob
    head.Initialize("", Me)
    head.Head(URL)
    head.Username = Tracker.UserName
    head.Password = Tracker.Password
    Wait For (head) JobDone (head As HttpJob)
    head.Release 'the actual content is not needed
    Tracker.hErrorMessage = head.ErrorMessage
    Tracker.hStatusCode =...

Elric

Well-Known Member
Licensed User
Thank you Erel!

This is the way I'm following.

For my benefit, I've tried to implement a new sub I've named "Download2" (compared to Download sub, I've added the following lines referring to the following code: 3-6, 11-14, 17-18, 32-35, 48-49, 65-69):
B4X:
Sub Class_Globals
    Type RangeDownloadTracker (CurrentLength As Long, TotalLength As Long, Completed As Boolean, Cancel As Boolean)
    Public hErrorMessage As String
    Public jErrorMessage As String
    Public hStatusCode As String
    Public jStatusCode As String
End Sub
(...)
' As Download but may set UserName and Password and get ErrorMessage and StatusCode from OkHttpUtils
Public Sub Download2 (Dir As String, FileName As String, URL As String, Tracker As RangeDownloadTracker, UserName As String, Password As String) As ResumableSub
    hErrorMessage = ""
    jErrorMessage = ""
    hStatusCode = ""
    jStatusCode = ""
    Dim head As HttpJob
    head.Initialize("", Me)
    head.Username = UserName
    head.Password = Password
    head.Head(URL)
    Wait For (head) JobDone (head As HttpJob)
    head.Release 'the actual content is not needed
    If head.Success Then
        Tracker.TotalLength = head.Response.ContentLength
        If Tracker.TotalLength = 0 Then Tracker.TotalLength = GetCaseInsensitiveHeaderValue(head, "content-length", "0")
'        Log(head.Response.GetHeaders.As(JSON).ToString)
        If GetCaseInsensitiveHeaderValue(head, "Accept-Ranges", "").As(String) <> "bytes" Then
            Log("accept ranges not supported")
            Tracker.Completed = True
            Return False
        End If
    Else
        Log($"Error: ${head.ErrorMessage}"$)
        Log($"Status code: ${head.Response.StatusCode}"$)
        hErrorMessage = head.ErrorMessage
        hStatusCode = head.Response.StatusCode
        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.Username = UserName
        j.Password = Password
        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
        Else
            Log($"Error: ${j.ErrorMessage}"$)
            Log($"Status code: ${j.Response.StatusCode}"$)
            jErrorMessage = j.ErrorMessage
            jStatusCode = j.Response.StatusCode
        End If
        j.Release
        If good = False Or Tracker.Cancel = True Then
            Tracker.Completed = True
            Return False
        End If
    Loop
    out.Close
    Tracker.Completed = True
    Return True
End Sub

I'm wondering why the head.ErrorMessageand j.ErrorMessage return a string only if the domain address is wrong (e.g. https://xyzsabnzbd.org/tests/internetspeed/20MB.bin) but is empty if the file name to be downloaded doesn't exist (e.g. https://sabnzbd.org/tests/internetspeed/20MBXYZ.bin, reason why I've also added "hStatusCode" and "jStatusCode" as variables.

I'm wondering also if could be better define "head" and "j" variables as public... or maybe not.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
I'm wondering why the head.ErrorMessageand j.ErrorMessage return a string only if the domain address is wrong (e.g. https://xyzsabnzbd.org/tests/internetspeed/20MB.bin) but is empty if the file name to be downloaded doesn't exist (e.g. https://sabnzbd.org/tests/internetspeed/20MBXYZ.bin, reason why I've also added "hStatusCode" and "jStatusCode" as variables.
Probably depends on the server configuration.

Better to extend RangeDownloadTracker type and add these fields. Otherwise, your code will not properly support concurrent downloads.
 
Upvote 0

Elric

Well-Known Member
Licensed User
Thanks.

Do you mean
B4X:
Sub Class_Globals
    Type RangeDownloadTracker (CurrentLength As Long, TotalLength As Long, Completed As Boolean, Cancel As Boolean, UserName As String, Password As String, _
        hErrorMessage As String, hStatusCode As Int, jErrorMessage As String, jStatusCode As Int)
End Sub

Public Sub Download2 (Dir As String, FileName As String, URL As String, Tracker As RangeDownloadTracker) As ResumableSub
    Dim head As HttpJob
    head.Initialize("", Me)
    head.Head(URL)
    head.Username = Tracker.UserName
    head.Password = Tracker.Password
    Wait For (head) JobDone (head As HttpJob)
    head.Release 'the actual content is not needed
    Tracker.hErrorMessage = head.ErrorMessage
    Tracker.hStatusCode = head.Response.StatusCode
    If head.Success Then
        Tracker.TotalLength = head.Response.ContentLength
        If Tracker.TotalLength = 0 Then
            Tracker.TotalLength = GetCaseInsensitiveHeaderValue(head, "content-length", "0")
        End If
'        Log(head.Response.GetHeaders.As(JSON).ToString)
        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)
        j.Username = Tracker.UserName
        j.Password = Tracker.Password
        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)
        Tracker.jErrorMessage = j.ErrorMessage
        Tracker.jStatusCode = j.Response.StatusCode
        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
        Else
            Log($"Error: ${j.ErrorMessage}"$)
            Log($"Status code: ${j.Response.StatusCode}"$)
        End If
        j.Release
        If good = False Or Tracker.Cancel = True Then
            Tracker.Completed = True
            Return False
        End If
    Loop
    out.Close
    Tracker.Completed = True
    Return True
End Sub
?
 
Last edited:
Upvote 0
Solution
Top