Android Question Get name of file from server?

aidymp

Active Member
Licensed User
Hi,

OK so for a while I have been making apps that download files and as long as the link ends in a known extension its pretty reliable. however my users are now using dropbox and tinyurl combinations, I would like to implement it as it makes the users life easy. the question is!!

In most web browsers entering a tinyurl (to a direct file link) the browser will open the save window and show the correct file name!

So far I have always used the URL stripped down to the name and extension this has always worked but with all URL shorteners the file name isn't there! So i don't know the Name or the extension.

Obviously the server is supplying the name to web browsers so how can I read it?

Thanks
 

aidymp

Active Member
Licensed User
Hi, Sorry I was a little vague! (I have several Download methods in the app, and Im not sure why lol)
I'm using okhttp and a modified hc_ResponseSuccess

B4X:
Sub hc_ResponseSuccess (Response As OkHttpResponse, TaskId As Int)
    ' ********** Modified code *************
    Dim cs As CountingOutputStream
    'file.openoutput()
    cs.Initialize(File.OpenOutput(TempFolder, TaskId, False))
    Dim j As HttpJob = TaskIdToJob.Get(TaskId)
    Dim Headers As Map=Response.GetHeaders '####################### Added this line
    Response.GetAsynchronously("response", File.OpenOutput(TempFolder, TaskId, False), True, TaskId) '####################### Added this line
    If j.Tag Is JobTag Then
        Dim jt As JobTag = j.Tag
        jt.CountingStream = cs
        jt.Total = Response.ContentLength
        If jt.Data.url = "" Then
            Log("Job cancelled before downloaded started")
            cs.Close
        End If
    End If
    '######################################### Added this and 2 lines above
    Dim i As Int
    For i=0 To Headers.Size-1
    Log(Headers.GetKeyAt(i)&" - "&Headers.GetValueAt(i))
    Next
    '**************************************
End Sub

I was hoping something would say filename! lol but i just get this

keep-alive - [timeout=5, max=82]
last-modified - [Fri, 15 Mar 2019 15:23:04 GMT]
server - [Apache/2.4.7 (Ubuntu)]
accept-ranges - [bytes]
connection - [Keep-Alive]
content-length - [12348]
content-type - [image/png]
date - [Sun, 07 Apr 2019 21:43:17 GMT]
etag - ["303c-584239d758600"]

This is as far as I get!

Thanks

Aidy
 
Last edited:

DonManfred

Expert
Licensed User
- I´m pretty sure it is a mistake to use a modified hc
- The httpjob has properties to get the Responseobject in JobDone. Incl. headers.

B4X:
    If job.Success Then
        Dim resp As OkHttpResponse = job.Response
        Dim list1 As List
        list1 = resp.GetHeaders
        For i = 0 To list1.Size - 1
            Log(list1.Get(i))
        Next
    End If
 

aidymp

Active Member
Licensed User
Ah! the problem seems to stem from the fact I am using the modules and NOT the library Hence the modified hc that was actually suggested by Erel! ;) . I need to use the modules because I need the progress event sub.

So adding @DonManfred 's code gives the error Unknown Member on response in relation to
B4X:
Dim resp As OkHttpResponse = job.Response
I did try the library version (with no progress event exposed) and that ran ((i didnt check if it did what I wanted) as I really do need the progress event)

UPDATE #############

I started with the modules from https://www.b4x.com/android/forum/threads/download-huge-files-with-httputils2.30220/ with the modified hc

I then realised they where out of date. As there was no .response object!

So moved onto https://www.b4x.com/android/forum/threads/b4x-okhttputils2-ihttputils2-httputils2-source-code.82632/

The code now runs but I get.
httputils2service_hc_responsesuccess (java line: 189)
java.lang.NullPointerException: Attempt to invoke virtual method 'okhttp3.Headers okhttp3.Response.headers()' on a null object reference
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$OkHttpResponse.GetHeaders(OkHttpClientWrapper.java:572)
at com.radianware.raddl.httputils2service._hc_responsesuccess(httputils2service.java:189)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
at anywheresoftware.b4a.BA$2.run(BA.java:370)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

No matter how I try to read the object!

Any tips?

Thanks
 
Last edited:

aidymp

Active Member
Licensed User
I have updated the .bas modules with the library source v2.70 in the Large file download example and modified the hc, all fine I then added the code suggested by @DonManfred It produces the same error, Does that mean that headers are not given by the server? or there is a bug in my code? the updated large file example is attached to this post and the code you suggested is in the Main module at line 46.
 

Attachments

DonManfred

Expert
Licensed User
Does that mean that headers are not given by the server?
you are not calling any webapi there. You are accessing the file directly from the server. The server does not send any additional headers.
you already know the name as it is part of the downloadurl. Why not use that name? That´s what a browser will do....
 

aidymp

Active Member
Licensed User
you are not calling any webapi there. You are accessing the file directly from the server. The server does not send any additional headers.
you already know the name as it is part of the downloadurl. Why not use that name? That´s what a browser will do....
The whole point of the question is I do not know the filename or type!?

In the original question I asked about getting the file name that my browser produces for urls like http://tinyurl.com/y3lr927 my browser gives the exact file name! similarly https://go.onelink.me/app/1f84795 chrome also produces the correct filename for this.

The above links have been modified to be broken! As im not the file owner.

How do I get the filename from the server?

Thats the question i need answering.

Thanks
 

DonManfred

Expert
Licensed User

DonManfred

Expert
Licensed User
similarly https://go.onelink.me/app/1f84795 chrome also produces the correct filename for this.
This are the header my browser gets while requestin this URL

Content-Type: application/octet-stream
Date: Thu, 11 Apr 2019 10:30:20 GMT
Location: https://downloads.nordcdn.com/apps/...nk=1f847954&pid=nordvpn.com&c=download_tv_apk
Set-Cookie: af_id=7c0710eb-a7e6-4b8a-b3ca-45abd6c30bde;Expires=Sat, 10 Apr 2021 10:30:20 +0000;Domain=onelink.me;Path=/
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Length: 0
Connection: keep-alive
Note that the filename is in the URL....

I expect this header to appear in the result. Will try later when @home.
 

aidymp

Active Member
Licensed User
This are the header my browser gets while requestin this URL



Note that the filename is in the URL....

I expect this header to appear in the result. Will try later when @home.
this is exactly what I require. however I just get a crash...
 

DonManfred

Expert
Licensed User
B4X:
    Log($"Download https://go.onelink.me/app/1f84795"$)
    Dim j As HttpJob
    j.Initialize("",Me)
    j.Download("https://go.onelink.me/app/1f84795")
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Log($"Job.Success = true"$)
        Dim resp As OkHttpResponse = j.Response
        Dim list1 As List
        list1 = resp.GetHeaders
        For i = 0 To list1.Size - 1
            Log(list1.Get(i))
        Next
    Else
        Log($"Job.Success false: ${j.ErrorMessage}"$)
    End If
Logger connected to: 988ad036525346515630
--------- beginning of main
--------- beginning of system
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Download https://go.onelink.me/app/1f84795
** Activity (main) Resume **
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
ResponseError. Reason: Bad Request, Response: app_not_found
Job.Success false: app_not_found
 

aidymp

Active Member
Licensed User
The problem seems to be the website go.onelink.me resp. their answer to the request.
the link was edited! (i did say) but then in the editor i only changed the link text not the link! so it actually ends in 4! however the crash still happens my end.

my log

** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
Download https://go.onelink.me/app/1f847954
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
Job.Success = true
main$ResumableSub_btnDownload_Clickresume (java line: 434)
java.lang.NullPointerException: Attempt to invoke virtual method 'okhttp3.Headers okhttp3.Response.headers()' on a null object reference
at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$OkHttpResponse.GetHeaders(OkHttpClientWrapper.java:572)
at b4a.example.main$ResumableSub_btnDownload_Click.resume(main.java:434)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:250)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:190)
at anywheresoftware.b4a.keywords.Common$11.run(Common.java:1179)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
java.lang.NullPointerException: Attempt to invoke virtual method 'okhttp3.Headers okhttp3.Response.headers()' on a null object reference
 
Last edited:

OliverA

Expert
Licensed User

OliverA

Expert
Licensed User
After changing @DonManfred's code to use a map and using your updated link, I have no issues retrieving the headers of the response (B4A v8.80). The issue now I think is that this is a redirect and the final headers retrieved by OkHttpUtils2 and served to the application do not include the Location header information and no hints of a filename are included.

B4X:
    Log($"Download https://go.onelink.me/app/1f847954"$)
   Dim j As HttpJob
   j.Initialize("",Me)
   j.Download("https://go.onelink.me/app/1f847954")
   Wait For (j) JobDone(j As HttpJob)
   If j.Success Then
       Log($"Job.Success = true"$)
       Dim resp As OkHttpResponse = j.Response
       Dim list1 As Map
       list1 = resp.GetHeaders
       For Each header As String In list1.Keys
           Log(header & ": " & list1.Get(header))
       Next
   Else
       Log($"Job.Success false: ${j.ErrorMessage}"$)
   End If
Logger connected to: T0622038I4
--------- beginning of system
--------- beginning of main
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Download https://go.onelink.me/app/1f847954
** Activity (main) Resume **
*** Service (httputils2service) Create ***
** Service (httputils2service) Start **
Job.Success = true
accept-ranges: [bytes]
cache-control: [public, max-age=14400]
cf-cache-status: [HIT]
cf-ray: [4c5dd9ca1f81ceca-IAD]
content-length: [30335422]
content-type: [application/octet-stream]
date: [Thu, 11 Apr 2019 15:04:02 GMT]
etag: [5c9354dc1cee1be]
expect-ct: [max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"]
expires: [Thu, 11 Apr 2019 19:04:02 GMT]
last-modified: [Thu, 21 Mar 2019 09:09:48 GMT]
server: [cloudflare]
set-cookie: [__cfduid=dff79ead373f5630fcf80330031ba729a1554995042; expires=Fri, 10-Apr-20 15:04:02 GMT; path=/; domain=.nordcdn.com; HttpOnly]
vary: [Accept-Encoding]
x-accept-before: [1555033370]
x-authorization: [key-id="rsa-key-1",algorithm="rsa-sha256"]
x-digest: [6fed03ef1a389678ac1acb491f4902be47fc140403a8ebe13141ae5f1fd92092]
x-frame-options: [SAMEORIGIN]
x-signature: [QcPq1rCeaCzfOKrrOV94jD7FMUibecGXy8YXQKxspYS8r0D3SWmfLK1iHBYhxawNi8Il9laTocS1Fe35vO71oKZBBG/xpUzs9Icrgs40XcKF6S8KPBYE2YmqlavjhDjhmA/c3kPEtmS5aF71ldMyeHfQWWRAIY1z5XPp3E6ZswrrkhsJ5PFS0R/k8ryInXojQlB62HDWIGmya89wdOFRWN2hSBubZoQ73zm113/ePkROLVu5oGWDXGy3k2aUQaDd2/SVtB0+QiuzFf8OYkcVHXyUMUIUscgMSH5rhHDBF1GHBGXGS6pukebdg51rj86CE2FM3zqRHyN6nP1GiYoybivwezs9F41xL+ZvyNSisTLqk1GheGynWThwbEEHLXb76N2FgmhiKwAMh1kvtND11h9mogpGen8MylxxEQViBHjCn8tS9JlkH60tgkF1jUdNP6bX1c+QuEWJqYkvN+j/6Pe1iX1ml3KfB8BGzWObO+kruQ9IvyUVWvHF5jlQLLOSJTt+SWt1L5W0ttw5nxL61VfcDb4rQG3NCggJvO3VioCwWtgzNLQL9IVxOFppPULXbKL5xrzZXX8nuKJ8zI/okfLS4EwsGDjFZCPwnL0HREwngWjGv8QeTNkfaiB04uwfeSe8OTtMb4afcT3PTqk5Hi7UtfyY7UQo/ITuy65kPoY=]
** Activity (main) Pause, UserClosed = false **
 

OliverA

Expert
Licensed User
I don't think you can do a GetHeaders in hc_responseSuccess. I'm guessing here, but I think the Response object is not fully populated until after this call
B4X:
Response.GetAsynchronously("response", File.OpenOutput(TempFolder, TaskId, False), True, TaskId) '####################### Added this line
Even if you did the Response.GetHeaders after this call, I still think/guess it would fail, since the async call may not be completed (it is async after all). Therefore you should only access the headers of the Response object outside of the HttpUtils module
 

aidymp

Active Member
Licensed User
OK i will try the above. Maybe if I can get just the file type that would be enough. But obviously the information is returned somewhere as all web browsers return the correct filename..
 

OliverA

Expert
Licensed User
If you transplant the hc_ResponceSuccess from the "Download huge files..." link (https://www.b4x.com/android/forum/threads/download-huge-files-with-httputils2.30220/) to the updated source, you just needed to add 1 line to get it current (well, to 2.70 standards, since this is the source available as of this post):
B4X:
Sub hc_ResponseSuccess (Response As OkHttpResponse, TaskId As Int)
   ' ********** Modified code *************
   Dim cs As CountingOutputStream
   cs.Initialize(File.OpenOutput(TempFolder, TaskId, False))
   Dim j As HttpJob = TaskIdToJob.Get(TaskId)
   Dim jt As JobTag = j.Tag
   jt.CountingStream = cs
   jt.Total = Response.ContentLength
   If jt.Data.url = "" Then
       Log("Job cancelled before downloaded started")
       cs.Close
   End If
   j.Response = Response ' <=================== Brings sub up to 2.70 standard
   Response.GetAsynchronously("response", cs , _
       True, TaskId)
   '**************************************
End Sub
And as before (in the plain 2.70 code), Response.GetAsynchronously is the last call (for a reason). If you absolutely had to access the headers in HttpUtils2Services, then you need to do it in Response_StreamFinish at the earliest (they should be accessible any time after that).
 
Top