HTTP File Download Without User Interaction Using .NET - The Article (Page 2 of 3 )
The idea is to make the server read the contents of the file from its hard disk and write it into the Response stream of HTTP. A control or a component residing on the client machine must read the file from the Response stream and write it into the client's hard disk. Server implementation of the idea is very easy; with the help of ASP.NET we can do it in exactly 8 lines.
The difficult part is the client implementation, how do we make a piece of code execute on the client machine which is stored on the server. For this we look at some of the solutions provided by two software giants
1. Activex control - which is a Microsoft technology 2. Applet - which is a Sun technology 3. .NET client side control - which is also a Microsoft technology
The main disadvantage with writing a .NET client side custom control is that .NET framework must be installed on every client machine for it to work .We can't expect every user to have .NET framework on their system, so we can't go for this solution.
We can write a control through java applets which solves our problem. For this, we got to use the "URL" and "URLConnection "object to open the connection to the URL and this "URLConnection" object provides a method by which we can get the response stream. Similarly we got to read from the response stream using "read" method.
Once the applet has been created we can include it in an HTML page through the "Applet" tag. From technology point of view, this is much simpler than writing an ActiveX control but a problem might arise if Microsoft doesn’t support it in the future or introduces a patch for it. This may lead to rewriting of the code.
Thus the best approach would be to use Microsoft technology, so we will concentrate only on ActiveX control as it is currently supported by .NET.
About ActiveX Control
The term 'ActiveX' refers to an extension of existing OLE and COM technologies and an ActiveX control is an object that supports a customizable, programmatic interface. Using the methods, events, and properties exposed by a control, Web authors can automate their HTML pages. Examples of such controls include text boxes, command buttons, audio players, video players, stock tickers, and so on.
The following are the possible solutions which can be used to solve our problem but I will explain the problems of first two methods and why we are going for the third method.
1. INET control - This is an ActiveX control provided by Microsoft for internet transfer of files. It provides a method called getChunk where we can get chunks of data from the response stream and openUrl method where we can get the entire data from the response stream but this control has some problem while downloading files.
It most often then not, gives a "Runtime error 13 type mismatch" message. This is a known problem and the web is flooded with people asking for help regarding this. Another problem with this control is, it doesn’t provide the number of bytes being downloaded so we can't give an accurate display of progress being made.
2 Winsock control - This is an ActiveX control provided by Microsoft for network programming. It uses sockets, to connect to the server and get the files. It works fine but for performance and programmer friendliness. There are delays in connecting to the server and the opening and closing of sockets has to be done by the programmer in order to prevent resource leaks. It's basically a low level control where the programmer has to do the bulk of the work.
3. Wininet dll - This DLL contains a set of API's provided by Microsoft for internet transfer of files. Performance is better in this case since we are directly using the API's and not relying on another control to provide the functionality. It has no problems as far as downloading of files is concerned so we would be using this DLL to solve our problem.
1. VB6 for client side code 2. .NET framework for server side code 3. Window 2000 or NT and above
Server Side Code
As I said before, we would need to write just 8 lines of code.
1. Create a new ASP.NET project with VB.NET as code behind and name it as MyDownload.
2. Add a new webform called download.aspx and paste this piece of code in the aspx file page_load procedure.
Private Sub Page_Load( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Response.ContentType = "application\octet-stream" Dim filename as string=new string("c:\\downloads\\hi.doc") Dim downloadFile As System.IO.FileStream = New System.IO.FileStream(filename, IO.FileMode.Open) Response.Write(downloadFile.Length() & "#") downloadFile.Close() Response.WriteFile(filename) Response.Flush() Response.End() End Sub
The logic behind this code is to first write the file length into response stream with "#" as delimiter between the file length and the data, followed by the file content into the response stream.
We are passing the file length in order to show an accurate display of progress made in the progress bar when downloading takes place. It's very important and a good programming practice to close the file stream and response stream after using it.
3. Build the project and it's all set as far as server side is concerned.
Client Side Code
1. Create a new ActiveX control VB project called FileDownload.
2. Before proceeding to write the code for downloading the file, we need to add the API declaration of wininet.dll to the project. So add a new module called wininet API and paste the following code in it.
Public Const INTERNET_OPEN_TYPE_PRECONFIG = 0 Public Const INTERNET_OPEN_TYPE_DIRECT = 1 Public Const INTERNET_OPEN_TYPE_PROXY = 3 Public Const scUserAgent = "VB OpenUrl" Public Const INTERNET_FLAG_RELOAD = &H80000000 Public Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _ (ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, _ ByVal sProxyBypass As String, ByVal lFlags As Long) As Long Public Declare Function InternetOpenUrl Lib "wininet.dll" Alias "InternetOpenUrlA" _ (ByVal hOpen As Long, ByVal sUrl As String, ByVal sHeaders As String, _ ByVal lLength As Long, ByVal lFlags As Long, ByVal lContext As Long) As Long Public Declare Function InternetReadFile Lib "wininet.dll" _ (ByVal hFile As Long, ByVal sBuffer As String, ByVal lNumBytesToRead As Long, _ lNumberOfBytesRead As Long) As Integer Public Declare Function InternetCloseHandle Lib "wininet.dll" _ (ByVal hInet As Long) As Integer
3. Change the name of the user control to FileDownloadCtrl.
4. Add a frame to the user control and place the following labels and the progress bar inside the frame.
a) lblByteD- set its caption to "Bytes Downloaded". b) lblBytesDownloaded- set its caption to "". c) lblFileS- set its caption to "FileSize in Bytes". d) lblFileSize- set its caption to "". e) pgDownload - set progressbar name to "pgDownload"
5. Use the "activex control interface wizard" in vb6's add-in menu to create a new function called downloadFile with the following declaration:
Public Function downloadFile(url As String, filename As String, ByRef errno As Integer) As Boolean
url - is the url of your download.aspx file e.g.
filename - is the name with which the downloaded file has to be saved as in the client machine.
errno - gives the errorno if any error has occurred.
True or false depending on whether the operation was success or not
6 Once you have finished all the steps in the wizard. It would have generated lots of code. Find downloadFile function in the code and do the following
Write the error handling statement and add the following variables
On Error GoTo errhandle Dim hOpen As Long Dim hOpenUrl As Long Dim sUrl As String Dim bDoLoop As Boolean Dim bRet As Boolean Dim sReadBuffer As String * 2048 Dim lNumberOfBytesRead As Long Dim sBuffer As String Dim percent As Integer Dim file As String Dim flen As Long
Next step is to open a connection to the URL passed as the parameter. This is done with the help of two API's "InternetOpen" and "InternetOpenUrl" provided by wininet.dll
Once the connection is obtained, we got to read the file size from the response stream which would be done by reading byte by byte till we reach "#" character. To read from the response stream we use another API of wininet dll called "InternetReadFile".This function takes 4 parameters
ohOpenUrl -the return value of "InternetOpenUrl" osReadBuffer - buffer into which the data has to be written from the response stream o1 - no of bytes to read which is specified by us; in our case it is one olNumberOfBytesRead - it is the actual bytes which was read
bDoLoop = True 'get file size While bDoLoop bRet = InternetReadFile(hOpenUrl, sReadBuffer, 1, lNumberOfBytesRead) If (Left$(sReadBuffer, 1) = "#") Then bDoLoop = False Else file = file & Left$(sReadBuffer, 1) End If Wend
Next step is to set the value of progress bar initially to "0" then increase as the bytes are downloaded. We also set the filesize label to its value.
We now come to main part, downloading of the file from the stream. We will use a simple while loop which repeatedly called "InternetReadFile" function, till number of bytes read is zero.
bDoLoop = True Dim cnt As Long cnt = 0 While bDoLoop sReadBuffer = vbNullString bRet = InternetReadFile(hOpenUrl, sReadBuffer, Len(sReadBuffer), lNumberOfBytesRead) sBuffer = sBuffer & Left$(sReadBuffer, lNumberOfBytesRead) percent = Int((Len(sBuffer) / flen) * 100) cnt = cnt + lNumberOfBytesRead If (cnt > 10000) Then lblBytesDownloaded.Caption = CStr(Len(sBuffer)) lblBytesDownloaded.Refresh cnt = 0 End If pgDownload.Value = percent if Not CBool(lNumberOfBytesRead) Then bDoLoop = False End If Wend
Cnt variable is used to refresh the bytesdownloaded label for every 100kb read, since progress bar's value property expects the value to be between 1 and 100, we calculate the percentage of bytes read by dividing the total bytes read by the file size.
The last step is to save the file to hard disk. For this we use open function and write the content of "sBuffer" into the file
Open filename For Binary Access Write As #1 Put #1, , sBuffer Close #1
We set the value of progress bar to 100 only here, since there might be some delay in writing to the hard disk. We will also close the connections, in order to prevent any memory leak once the work is done.
pgDownload.Value = 100 lblBytesDownloaded.Caption = file lblBytesDownloaded.Refresh If hOpenUrl <> 0 Then InternetCloseHandle (hOpenUrl) If hOpen <> 0 Then InternetCloseHandle (hOpen)
7. With the above steps, we have finished developing the code for downloading the files and only the creation of the ActiveX control remains. To do it, click on File menu of VB IDE and select "make filedownload.ocx", click on it and voila!!, your ActiveX control is ready.
8. For the control to be installed on the client machine, we need to package it into CAB file. This can be done with the help of "Package and Deployment Wizard" provided by Visual Studio6.
10. The one thing that you have to keep in mind is you need to sign the ActiveX control, for it to be installed on the client machine without changing the security settings of Internet Explorer.
If you are planning to give an unsigned ActiveX control then on the client machine' Internet explorer, you have to select,