URLDownloadToFile with original file name?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Sergio
Posts: 45
Joined: 29 Sep 2013, 16:36

URLDownloadToFile with original file name?

27 Sep 2016, 11:46

I want to use AHK to create a small UI for querying an API. The API generates a random UUID for each download & I just need to know the name of the file which I've downloaded.

The problem is that URLDownloadToFile forces you to specify the filename upon downloading. Is it possible to get the file name as it was originally named by the server & not by AHK?
garry
Posts: 3764
Joined: 22 Dec 2013, 12:50

Re: URLDownloadToFile with original file name?

27 Sep 2016, 12:38

example

Code: Select all

url:="http://www.replicaradio.ro/audio/billboard/1960//02.Jim%20Reeves-He'll%20Have%20To%20Go.mp3"
SplitPath, url, name, dir, ext, name_no_ext, drive
F1=%a_desktop%\%name%
urldownloadtofile,%url%,%f1%
return
Sergio
Posts: 45
Joined: 29 Sep 2013, 16:36

Re: URLDownloadToFile with original file name?

27 Sep 2016, 13:11

Thanks Garry.

I just want to reiterate that it is an API & I do not know at the time of the request what the filename of the response will be. So my request is a lot like this:

Code: Select all

http://thesite.com/api/foo?paramter=bar
And the response file is named:

Code: Select all

74bfb59b-8410-42a7-83ad-fca268a5b49c.xlsx
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: URLDownloadToFile with original file name?

27 Sep 2016, 13:45

If you use a http request ( or similar request object ),
the filename may or may not be in the response header.

You can 1st test this by returning and viewing the entire response header:

Code: Select all

url = http://thesite.com/api/foo?paramter=bar

ReqObj := ComObjCreate( "WinHttp.WinHttpRequest.5.1" )
ReqObj.Open( "GET", url ), ReqObj.Send()

msgbox % ReqObj.GetAllResponseHeaders()
If you see the file name within the header,
you can use it's `item name` in the GetResponseHeader() method to return the specific name, for instance:
EXAMPLE RESPONSE HEADER:
Connection: Keep-Alive
Date: Tue, 27 Sep 2016 18:35:41 GMT
Keep-Alive: timeout=5, max=100
Content-Length: 100
Content-Type: text/plain
Content-Disposition: 74bfb59b-8410-42a7-83ad-fca268a5b49c.xlsx
Last-Modified: Tue, 27 Sep 2016 18:30:39 GMT
Accept-Ranges: bytes
ETag: "9-123456789"
Server: Some Server Type
In this case you'd use msgbox % ReqObj.GetResponseHeader("Content-Disposition")
You can then stream/save the response body to a file using a ADODB stream object.

just an idea...
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: URLDownloadToFile with original file name?

28 Sep 2016, 02:46

The other case is the url redirect to another url.

Code: Select all

MsgBox, % GetUrlFilename("https://autohotkey.com/download/ahk-install.exe") ; The url redirect to another url
MsgBox, % GetUrlFilename("https://wordpress.org/latest.zip") ; The filename specified in the Content-Disposition response header

GetUrlFilename(url) {
	whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	whr.Open("HEAD", url)
	whr.Send()
	try {
		RegExMatch( whr.GetResponseHeader("Content-Disposition"), "filename=\K.+", m )
	} catch {
		RegExMatch( whr.Option(1), "[^/]+$", m ) ; 1 = WinHttpRequestOption_URL
	}
	return m
}

Code: Select all

WinHttp_UrlDownloadToFile(URL, Filename := "") {
	WHR := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	WHR.Open("GET", url, true)
	WHR.Send()
	WHR.WaitForResponse()

	if (Filename ~= "^\s*$") {
		try {
			RegExMatch( WHR.GetResponseHeader("Content-Disposition"), "filename=\K.+", Filename )
		} catch {
			RegExMatch( WHR.Option(1), "[^/]+$", Filename ) ; 1 = WinHttpRequestOption_URL
		}
	}

	ADO := ComObjCreate("ADODB.Stream")
	ADO.Type := 1 ; adTypeBinary
	ADO.Open()
	ADO.Write(WHR.ResponseBody)
	ADO.SaveToFile(FileName, 2)
	ADO.Close()
}
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: URLDownloadToFile with original file name?

28 Sep 2016, 18:03

tmplinshi wrote:The other case is the url redirect to another url.
ahh WinHttpRequestOption_URL is very nice :)

On a side-note I'm lurking the spec to figure out if `Content-Disposition` is returned in a fixed order.
That way something like StrSplit() can be used over RegEx. Just a thought.
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: URLDownloadToFile with original file name?

28 Sep 2016, 20:35

TLM wrote:On a side-note I'm lurking the spec to figure out if `Content-Disposition` is returned in a fixed order.
That way something like StrSplit() can be used over RegEx. Just a thought.
But the delimiter = may appear in the filename.

StrSplit is actually a little slower than RegExMatch.

Running 10,000 times time-consuming (seconds):
0.004214 <- RegExMatch
0.008914 <- StrSplit

Code: Select all

#NoEnv
SetBatchLines, -1

str := "Content-Disposition: attachment; filename=wordpress-4.6.1.zip"

QPX(true)
	Loop, 10000 {
		RegExMatch(str, "filename=\K.+", m)
	}
time_RegExMatch := QPX(false)

QPX(true)
	Loop, 10000 {
		m := StrSplit(str, "=")[2]
	}
time_StrSplit := QPX(false)

MsgBox, % time_RegExMatch . " <- RegExMatch" . "`n"
        . time_StrSplit   . " <- StrSplit"

QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}
User avatar
lmstearn
Posts: 694
Joined: 11 Aug 2016, 02:32
Contact:

Re: URLDownloadToFile with original file name?

29 Jan 2018, 22:57

Redirects like 302 are a pain, because generated headers of non-existent files will download as files without the error being easily trapped.
If the file to be downloaded is not text/html content-type, then at least an invalid file will be picked up as one of those.
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH
User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: URLDownloadToFile with original file name?

30 Jan 2018, 07:41

(To be honest I didn't read everything, I only know what I've read in the title.)

This might be of some help.

Code: Select all

$F1::
	FileSelectFolder, Location
	Location := RegExReplace(Location, "\\$")
	InputBox, Link, URL, Please input a url to download.
	UrlDownloadToFile, % Link, Location "\" SplitPath(Clipboard).FileName
	Return

SplitPath(File) {
	If (IsObject(File)) {
		Files := []
		For Each, Item in File {
			SplitPath, Item, F, D, E, N, D_
			Files[Each] := {FileName: F, Dir: D, Ext: E, NameNoExt: N, Drive: D_}
		}
		Return, Files
	} Else {
		SplitPath, File, F, D, E, N, D_
		Return, {FileName: F, Dir: D, Ext: E, NameNoExt: N, Drive: D_}
	}
}

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat

garry
Posts: 3764
Joined: 22 Dec 2013, 12:50

Re: URLDownloadToFile with original file name?

30 Jan 2018, 08:40

easy example if no redirect

Code: Select all

url :="http://www.replicaradio.ro/audio/billboard/1960//02.Jim%20Reeves-He'll%20Have%20To%20Go.mp3"
urla:= uriDecode(url)    ;- remove %20 etc

SplitPath, urla, name, dir, ext, name_no_ext, drive
F1=%a_desktop%\%name%
urldownloadtofile,%url%,%f1%

ifexist,%f1%
  run,%f1%
return

uriDecode(str) {
   Loop
      If RegExMatch(str, "i)(?<=%)[\da-f]{1,2}", hex)
         StringReplace, str, str, `%%hex%, % Chr("0x" . hex), All
      Else Break
   Return, str
}

combined good REDIRECT example from tmplinshi & TLM ( see above )

Code: Select all

;--- redirect example tmplinshi / TLM ---------------
f1:="https://autohotkey.com/download/ahk-install.exe"
f2:="https://wordpress.org/latest.zip"

fd:=a_desktop  ;- save here
;----------------------------------------------------
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.SetTimeouts(3000,3000,3000,3000)
global whr,fd

;-- see header TLM ( if needed ) -------
whr.Open( "GET", f1 ), whr.Send()
  rh1:=whr.GetAllResponseHeaders()

;whr.Open( "GET", f2 ), whr.Send()
;  rh2:=whr.GetAllResponseHeaders()
;---------------------------------------

a1:=GetUrlFilename(f1)
a2:=GetUrlFilename(f2)

msgbox,%f1%`n%a1%`n-------------------`n%f2%`n%a2%`n`n-------HEADER from %f1%-------------`n%rh1%`n------------------------`n%rh2%

url:=WinHttp_UrlDownloadToFile(F1)
msgbox,downloaded file=`n%f1%`n%fd%\%a1%

;url:=WinHttp_UrlDownloadToFile(F2)
;msgbox,downloaded second file =`n%f2%`n%fd%\%a2%
;run,%fd%    ;- open folder
return

;------------- tmplinshi --------------------------------------
GetUrlFilename(url) {
	;whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	whr.Open("HEAD", url)
	whr.Send()
	try {
		RegExMatch( whr.GetResponseHeader("Content-Disposition"), "filename=\K.+", m )
	} catch {
		RegExMatch( whr.Option(1), "[^/]+$", m )       ;- 1 = WinHttpRequestOption_URL
	}
	return m
}
;------------ urldownloadtofile --------------------------------
WinHttp_UrlDownloadToFile(URL, Filename := "") {
	;WHR := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	WHR.Open("GET", url, true)
	WHR.Send()
	WHR.WaitForResponse()

	if (Filename ~= "^\s*$") {
		try {
			RegExMatch( WHR.GetResponseHeader("Content-Disposition"), "filename=\K.+", Filename )
		} catch {
			RegExMatch( WHR.Option(1), "[^/]+$", Filename )    ;- 1 = WinHttpRequestOption_URL
		}
	}

	ADO := ComObjCreate("ADODB.Stream")
	ADO.Type := 1 ; adTypeBinary
	ADO.Open()
	ADO.Write(WHR.ResponseBody)
	ADO.SaveToFile(fd . "\" . FileName, 2)
	ADO.Close()
}
;========================================================================


Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: CrowexBR, Google [Bot] and 270 guests