Hi,
Its been a while since my last blog post here at OS4Coding and I was hoping to start a series of tutorials, but due to time constraints and 'is it really worth it' and 'is anyone listening?' I finally decided to make another post for interested Hollywood developers.
In this blog I attach some source code for adding asynchronous downloads into your project(s).
This code was created out of a necessity to add support for asynchronous downloads into my application Jack.
I created the below code this weekend and hope to introduce it into Jack over the course of the next week and into next weekend.
The example code here will make a connection to OS4Depot and download 3 files simultaneously and save them to RAM.
C/C++ developers will notice straightaway its not very long, I can personally imagine doing the same thing in C/C++ would require much more work and this is the appeal of Hollywood to many users regardless of experience.
Foreword: This code is not optimised in any way and is purely my testing code before migrating it across into Jack where I can then add error handling (404 codes) and other such requirements of the project. This code is also particularly catered towards OS4Depot where HTTP Redirection is in place to reach the downloadable files- if you are going to use this code to download from elsewhere you may well have to do adapt the code accordingly; e.g. where HTTP redirection is not in place.
This example code allows for up to 30 concurrent downloads, but can easily be changed at a whim - and indeed I will no doubt reduce this to 10 or perhaps 15. I am positive Origin won't appreciate the server being overloaded!
I won't go into too much detail here in this blog on how it all works - suffice to say - if you have intermediate level of understanding of Hollywood it'll make perfect sense.
Basically we say which file we want to download using base:DownloadFile() and how big it is. The event handler 'OnReceiveData' spools the data in chunks of 65336 and writes them to disk, when the download is complete we close the connection and the file with SaveToDisk().
base:AllocateFindFilePort() is responsible for Allocating or Finding an available File Port an assigning it to the connection.
As all this is happening the program flow continues and the program can do whatever it needs to do between Repeat.. WaitEvent()... Forever.
Feel free to re-use or adapt my code as you see fit.
This completes my short blog on asynchronous downloading of files with Hollywood.
Here's the code...
; This asychronous download example code is adapted to OS4Depot where HTTP ; redirection is in place and must be adapted if the host site doesn't use ; HTTP redirection. Global base, data$, count, done, host$ host$ = "www.os4depot.net" base = {} ; Userdata, FilePort, Remote Archive, Bytes Received, Total Filesize DimStr connections[30][2][1][1][1] For Local c = 0 to 29 connections[c][0] = "" connections[c][1] = c connections[c][2] = "" connections[c][3] = 0 connections[c][4] = 0 Next Function base:AllocateFindFilePort(userdata) Local c, available, found If userdata = nil ; Allocate File Port For c = 0 to 29 If GetType(connections[c][0]) <> #LIGHTUSERDATA available = True Break EndIf Next ; If no connections available return Nil, maxconnections set in DimStr connections[] If available = False c = Nil EndIf Else ; Find File Port via userdata For c = 0 to 29 If GetType(connections[c][0]) = #LIGHTUSERDATA If connections[c][0] = userdata found = True Break EndIf EndIf Next If found = False c = Nil EndIf EndIf Return (c) EndFunction Function base:DownloadFile(server$, url$, downloadpath$, filename$, filesize) Local id = base:AllocateFindFilePort() Local data$, count, done connections[id][0] = OpenConnection(nil, server$, 80) SendData(connections[id][0], "GET " .. url$ .. " HTTP/1.0\r\n\r\n") ; This line here will not work if the site doesn't use redirection, you will need to ; adapt this accordingly. data$, count, done = ReceiveData(connections[id][0], #RECEIVEALL) ; Redirect If FindStr(data$,"302 Found") > -1 CloseConnection(connections[id][0]) redirection$ = MidStr(data$,FindStr(data$,"Location: "),StrLen(data$)) redirection$ = MidStr(redirection$,10,FindStr(redirection$,"\n")-11) server$ = ReplaceStr(redirection$, "http://", "") server$ = MidStr(server$,0,FindStr(server$,"/")) connections[id][0] = OpenConnection(nil, server$, 80) SendData(connections[id][0], "GET " .. redirection$ .. " HTTP/1.0\r\n\r\n") data$ = nil ; Skip HTTP Headers While data$ = nil Or FindStr(data$,"Content-Type:") = -1 data$, count, done = ReceiveData(connections[id][0], #RECEIVELINE) Wend data$, count, done = ReceiveData(connections[id][0], #RECEIVELINE) ; Skip blank line ; Adapt this for sites that don't use HTTP rdirection Else ) EndIf ; Not Found If FindStr(data$,"404 Found") > -1 Return(False) ; Found - Download it Else connections[id][1] = OpenFile(nil,downloadpath$ .. filename$, #MODE_READWRITE) connections[id][2] = filename$ connections[id][4] = filesize Return(True) EndIf EndFunction Function base:BuildFile(userdata) Local segment$ segment$, count, done = ReceiveData(userdata.id, #RECEIVEBYTES, 65536) ; Read file data Local port = base:AllocateFindFilePort(userdata.id) WriteString(connections[port][1],segment$) connections[port][3] = connections[port][3] + StrLen(segment$) If connections[port][3] = connections[port][4] Then SaveToDisk(userdata.id,port) EndFunction Function base:Keyboard(msg) Switch msg.action Case "OnKeyDown" If msg.key = "ESC" End() EndIf EndSwitch EndFunction Function base_BuildFile(msg) base:BuildFile(msg) EndFunction Function base_KeyboarD(msg) base:Keyboard(msg) EndFunction InstallEventHandler({OnReceiveData = base_BuildFile, OnKeyDown = base_Keyboard}) debugprint("Asynchronous file download demo by Richard Lake (richard@lakemarketingandevents.co.uk)") debugprint("\nWorks with sites like OS4Depot where HTTP Redirect is in place.") debugprint("\nOne limitation in this demo is that you must know beforehand the filesize of each download in advance.") debugprint("\nDownloading 3 files from OS4Depot asynchronously...") success = base:DownloadFile(host$,"http://" .. host$ .. "/share/game/board/africa.lha","RAM:","africa.lha",297517) success = base:DownloadFile(host$,"http://" .. host$ .. "/share/utility/misc/ipqalc.lha","RAM:","ipqalc.lha",114081) success = base:DownloadFile(host$,"http://" .. host$ .. "/share/emulation/gamesystem/amiarcadia.lha","RAM:","amiarcadia.lha",3454033) Function SaveToDisk(userdata,port) CloseConnection(userdata) CloseFile(connections[port][1]) debugprint(connections[port][2] .. " saved to RAM:") connections[port][0] = "" connections[port][2] = "" connections[port][3] = 0 connections[port][4] = 0 Endfunction Repeat WaitEvent() Forever
Comments
Submitted by Tedzogh (not verified) on
Great blog to examin for a soon to be HW owner.
Thanks
Submitted by stephenix1015 (not verified) on
This is just the tip of the iceberg. If you want to see some more, including function to develop AmigaOS-like applications complete with buttons, icon bars, option boxes and other event handling.-Missed Fortune