Getting Remote Files With ASP Continued

This second article will show how to send remote binary files, multiple files, and additional information with only ASP and XML. This is an incredibly powerful technique, and the implementation in this two part series will deal with automatically updating a deployed ASP web application.


There is a file available for download for this article here.

Introduction

The first article in this two-part series handled the client portion of transmitting files – even binary files – using only XML and ASP. This is a very powerful extension to my previous tutorial on how to retrieve remote pages and save them locally. Of course all the work we discussed in the last article is no good on the client side, without a server to process the request and encode the files to send back to the client.

Once again, this article is derived from code used for an impressive ‘auto-update’ feature to a popular e-commerce solution, Mall23 (http://www.mall23.com/store/Default.asp?vf=14). The deployed versions of the application can query the master server for updated versions, and download and install any modified files. Make sure you’ve read the previous article on how to send the request, process the response, and save the files.

But how does the server actually encode and send the files? Well, the first thing we start with is a query from the client application. As I mentioned previously, the ‘client’ is technically a server-based web application, but it is referred to as a client of the master application. This query can be any data you’d like to send the server, but in our example, the client is sending the sGenKey (license key) and the sCurrentVersion (the version the client is currently running).

It is advantageous to transmit a license key, as it can be checked against your database to validate it. This way you can ensure that the license accompanies software that has been paid for, and that the client’s auto-update subscription hasn’t expired. You could even match this to server names, to ensure that pirated versions of your application aren’t being leaked out. I’ll leave that up to your creative mind!

{mospagebreak title=Let’s Code}

What we need to do first is retrieve and parse the request from the query string. Then you can add in whatever simple or complex license checking algorithm you want in the indicated space. Then we call the subroutine – called PerformUpate() – that creates XML to return to the client.

Dim sGenKey, sCurrentVersion, fsoFileObject, objStream, xmlDOMDocument

If ( Request.ServerVariables(“HTTP_METHOD”) = “GET” ) Then

      sGenKey           = Trim(Request.QueryString(“sGenKey”))

      sCurrentVersion   = Trim(Request.QueryString(“sCurrentVersion”))

     

      ‘ do processing here to validate the License Key

     

      Set fsoFileObject = CreateObject(“Scripting.FileSystemObject”)

      Set objStream     = Server.CreateObject(“ADODB.Stream”)

      Set xmlDOMDocument= Server.CreateObject(“Msxml2.DOMDocument.4.0″)

      Call PerformUpdate()

     

      Set fsoFileObject = Nothing

      Set objStream     = Nothing

      Set xmlDOMDocument= Nothing

End If

{mospagebreak title=PerformUpdate() Explained}

Once we are sure we want to perform the update (the license key has checked out as legitimate), we can begin creating the XML document to send back to the client. In this example, the parent node of our XML is ‘MALL23’. You can customize this for your own application.

Within the parent node, there are three child nodes: MESSAGE, VERSION, and CODE.

  • The MESSAGE node holds error or success codes, or action codes for the client to perform.
  • The VERSION node holds the version that the client is being updated to. This way, the client will know what version it will be on once the update has been completed. As I mentioned in the previous article, it would be wise to store this in a local config file on the client, and read the version each time.
  • The CODE node is optional code to run on the client that is not part of the normal update. This can be used for many purposes, to activate or deactivate a client, grant or revoke administrator rights, rename or move files, reverse product SKUs (kidding!)…

Sub PerformUpdate()

      Dim oRoot, oElement, objFolder, objItem2

      xmlDOMDocument.loadXML “<MALL23 />”

     

      Set oRoot = xmlDOMDocument.documentElement

     

      Set oElement = xmlDOMDocument.createElement(“MESSAGE”)           

      oElement.nodeTypedValue = “error, success or action messages to send to the client”

      oRoot.appendChild oElement

     

      Set oElement = xmlDOMDocument.createElement(“VERSION”)

      oElement.nodeTypedValue = “version that the client is being update to”

      oRoot.appendChild oElement

     

      Set oElement = xmlDOMDocument.createElement(“CODE”)        

      oElement.nodeTypedValue = “misc. code to be processed by the client application”

      oRoot.appendChild oElement

     

      Set objFolder = fsoFileObject.GetFolder(Server.MapPath(“Updates/”))

      For Each objItem2 In objFolder.Files

            Call ProcessFile(objItem2.Path)

      Next

      Set objFolder = Nothing

      xmlDOMDocument.save(Response)

End Sub

For this example, the update files exist in a subfolder called “Updates”. You can change it to whatever you like. The most practical thing to do in a professional application with many subfolders is to put the exact subfolders within the Updates folder, and traverse through them. You can automate your code versioning system to place a copy of the updated files into their respective subfolders in the ‘update’ directory.

The PATH node (see the Client Side Parse section of the previous article) contains the relative folder path for each file anyway. That way, the exact folder structure will be transmitted and retained on the client version of the application. For the sake of simplicity, the example just loops through all the files in the ‘Updates’ folder, no subfolders.

You’ll notice that for each file in the ‘Updates’ folder, the ProcessFile() subroutine is called. Let’s examine that now.

{mospagebreak title=ProcessFile() Explained}

The ProcessFile() subroutine appends both the PATH and TRANSFERFILE (the encoded file itself) data to the XML message.

When adding the TRANSFERFILE node, .txt and .asp files are simply read as text files using FSO and appended to the XML as basic string nodes. If you have any other files that should be read as text (html files, js files, css files, vbs files, and so on), simply add their extension to the ‘if ( InStr(sPath,’ line. Binary files, however, need to be opened using ADO’s Stream object. Then the file is encoded as a Base64 text string, specified as such in the XML node, then appended to the document. This is accomplished with a free xmlDOMDocument object that Microsoft provides. Thanks Microsoft!!!

If you uncomment the following two debug lines client script (last article):

‘Response.Write sResponse

‘Response.End

the browser will print out the Base64 encoded text for you when you run the client script in your browser. You’ll notice it looks just like a big square of numbers and letters. The client receives this, and based on the filename in the PATH node, it knows if it’s a normal text file or a binary file. If it’s binary, the client reads the XML as Base64 and uses the ADO object to render the encoded file back into its raw binary form, as we saw in the first article.

When the binary file is read into memory using the ADO stream object, it is at that point that the dataType property is set to “bin.base64″.

Here’s the code for the subroutine:

Sub ProcessFile(sPath)

      Dim oRoot, oElement, fFile, tsTextStream

     

      Const ForReading = 1, TristateFalse = 0

      Set oRoot = xmlDOMDocument.documentElement

     

      Set oElement = xmlDOMDocument.createElement(“PATH”)        

      oElement.nodeTypedValue = sPath

      oRoot.appendChild oElement

      ‘Here comes the Binary data part

      Set oElement = xmlDOMDocument.createElement(“TRANSFERFILE”)

      If ( InStr(sPath, “.asp”) <= 0 ) And ( InStr(sPath, “.txt”) <= 0 ) Then

            objStream.Type = 1      ‘adTypeBinary

            objStream.Open

            objStream.LoadFromFile(sPath)

           

            oElement.dataType = “bin.base64″

            oElement.nodeTypedValue = objStream.Read

           

            objStream.Close

      Else

            ‘  just read it as a plain text file

            Set fFile = fsoFileObject.GetFile(sPath)

            Set tsTextStream = fFile.OpenAsTextStream(ForReading, TristateFalse)

            oElement.nodeTypedValue = tsTextStream.ReadAll

            tsTextStream.Close

      End If

      ‘ add it to the XML document

      oRoot.appendChild oElement

End Sub

{mospagebreak title=Conclusion}

When all the files have been processed, and appended to the XML document, it’s all ‘saved’ and thereby transmitted back to the requesting client with this one line in the PerformUpdate() routine:

xmlDOMDocument.save(Response)

The client application will now receive the response. The amount of time to transmit the updates will obviously vary depending on the size and number of files being transferred, and the connection between the two. Once the entire XML response has been transmitted, the parsing and saving of the files now entirely becomes the responsibility of the client application.

One word of caution, if you perform very complicated or large updates, you may need to break up the update process into several passes.

There are tons of really cool things you can do with this technique to extend it. You can hide your files and create a download script that exceeds the IIS 4meg metabase limit (this ought to chafe some hides). You can even use the server script to update Windows Applications (.exe’s) where the application uses Sockets to connect to the web and then to your server script. You can essentially manage an unlimited number of remote installations from a single console: turn features on or off, limit access, grant rights, add features, fix bugs, send messages, and throw parties (this was a suggestion of the original programmer, I’ll take his word for it!). Now that anyone can transfer any type of file and run any script, you’re pretty much limited only by your imagination!

For your convenience, I’m including a link to the full script and update folder, so that you don’t have to copy and paste from this article.

4 thoughts on “Getting Remote Files With ASP Continued

  1. I love the article and it was very informing. Is there a way to accomplish the same thing with ASP.NET?

[gp-comments width="770" linklove="off" ]