|
This Issue
In this issue of 15 Seconds we feature the final chapter, by Alex Homer, of a new book called 'Professional Active Server Pages 2 Programming' from Wrox Press (ISBN 1-861001-26-6) due for publication in early March 1998. The chapter as a whole is a case study examining how Active Server Components can be built from existing components, so as to achieve high levels of functionality reasonably quickly. This section describes how the finished component can be used.
ServerZip Component
As an intrepid programming detective, you've now discovered almost all there is to know about the ServerZip component all that remains of our adventure story is the obligatory happy ending. Even though you may not have built the component yourself, there's nothing to stop you using it in your own pages. In fact, there are now so many Active Server Components available that you may never have to build your own.
For some idea of the range of Active Server Components that are available, visit 15 Seconds' Component Section
In the remainder of this chapter, we'll be putting the ServerZip component to work. You can download the finished component from http://rapid.wrox.co.uk or http://www.rapid.wrox.com. Included with it are a series of sample pages that demonstrate the features it provides. These are designed to allow you full freedom to experiment with all the properties and methods that the component provides.
Warning
Allowing anonymous remote users free access to all the properties and methods is probably not a good idea. In your ASP applications, you'll want to expose only a selected set of properties and methods over the Web, and verify and set the others within your ASP script code. You can also limit the available options using the SZConfig utility that is provided with the component.
The ServerZip Sample Pages
In the section about the component interface, earlier in this chapter, we listed all the available properties and methods of the component. Here, we'll be using all these in a series of four sample pages:
Step 1 - Setting the file listing properties to display files on the server.
Step 2 - Displaying the files list and setting options for the doZip() method.
Step 3 - Creating a zip file, and setting the options to delete old files.
Step 4 - Deleting old download directories and returning to Step 1.
To run these pages, you must first have installed the ServerZip component (instructions are provided in the download file). Copy the samples from the installation directory into a directory somewhere below the WWWRoot directory on your server, and make sure that this directory has Script Execute access permission.
Setting the fileListing Properties
The first page, szexamp.htm, is not an ASP page at all, it just contains the controls where the user can set the values to be used for listing files:
Figure 1
The controls are on a <FORM>, whose ACTION is the file szfiles.asp. We use METHOD="POST" (as recommended by HTML 4.0), and so we'll be able to collect the values in the next page using the ASP Forms collection:
<FORM ACTION=szfiles.asp METHOD="POST">
<TABLE WIDTH=100% CELLPADDING=5>
...
<TR>
<TD>Directory name (physical or virtual):</TD>
<TD><INPUT TYPE="TEXT" NAME=txtFileRoot SIZE=25></TD>
<TD><B>= fileListRoot</B></TD>
</TR>
<TR>
<TDText/HTML to go before filename: </TD>
<TD><INPUT TYPE="TEXT" NAME=txtBeforeHTML SIZE=25 VALUE="<OPTION>"></TD>
<TD ALIGN="LEFT"><B>= beforeFileHTML</B></TD>
</TR>
<TR>
<TD>Text/HTML to go after filename: </TD>
<TD><INPUT TYPE="TEXT" NAME=txtAfterHTML SIZE=25></TD>
<TD><B>= afterFileHTML</B></TD>
</TR>
<TR>
<TD>File type specification:</TD>
<TD><B>fileListing(
<INPUT TYPE="TEXT" NAME=txtFileType SIZE=8 VALUE="*.*"> )</B></TD>
<TD><B>Method parameter</B></TD>
</TR>
<TR>
<TD COLSPAN=3 ALIGN="RIGHT"><INPUT TYPE="SUBMIT" VALUE="Next >"></TD>
</TR>
</TABLE>
</FORM>
The last row of the table is the SUBMIT button, which sends the values of all the controls to the file szfiles.asp.
Listing files and Setting the doZip() Options
The remaining three pages are all Active Server Pages .asp files. Because the ServerZip component works best with buffering turned on, we've included the following line at the beginning of each page:
<% Response.Buffer = True %>
In the page szfiles.asp, we use the component to create a list of files in a <SELECT> control this is why the default value of the beforeFileHTML property control in the previous page was set to <OPTION>. Here's the code that creates the new instance of our component, sets its properties, and calls the fileListing() method—again, all the controls are on a <FORM>, whose ACTION this time is the file szdozip.asp:
<FORM ACTION=szdozip.asp METHOD="POST">
...
<SELECT MULTIPLE SIZE=10 NAME=selSourceList>
<%
Set objSZ = Server.CreateObject("Stonebroom.ServerZip")
objSZ.fileListRoot = Request.Form("txtFileRoot")
objSZ.beforeFileHTML = Request.Form("txtBeforeHTML")
objSZ.afterFileHTML = Request.Form("txtAfterHTML")
intFiles = objSZ.fileListing(Request.Form("txtFileType"))
Set objSZ = Nothing
%>
</SELECT>
We also use the return value of the fileListing() method to show how many files were listed:
...
Listed <% = intFiles %> files.
...
Finally, we store the value of the fileListRoot property in a HIDDEN control, so that we can pass it on to the next page where we'll need it again:
...
<INPUT TYPE="HIDDEN" NAME="hidListPath" VALUE="<% = Request.Form("txtFileRoot") %>">
...
And here's the result. You can see we found three files:
Figure 2
The remainder of this page is just more HTML controls on the same <FORM> as the <SELECT> list. When the user clicks the Next button, the values of these controls will be passed on to the next page, szdozip.htm.
Creating a Zip File
Now we come to the most complicated page. This has to collect all the values sent from the previous page, and execute the doZip() method of the component. However, things are made more complex by the fact that we're allowing the user to select files using the list we provided. Alternatively, they can type a physical or virtual file specification into the text box below the list. So the first thing our code has to do in the szdozip.asp page is figure out what the sourceFileList property should be.
The value sent from the txtSourceList control contains the text typed into the sourceFileList text box. If there was a value there, we simply use it as the sourceFileList property. However if it was empty, we know that we have to use the items selected in the list instead. This is a multiple-select list (it includes the MULTIPLE attribute in its opening HTML tag), so the value could be more that one filename. In this case, the browser returns all the selected entries as a single string with each one separated from the rest by a comma.
Handling Multiple-Select Lists
Our ASP code has to break this list up into a format suitable for the component's sourceFileList property, and add the path that the user provided for the file listing to each one. This is why we carried it forward in the hidden control from the previous page. If the path is a virtual one we can only use one file, and this will be the first one selected. However if it's a physical path, we have to collect all the files from the list. The first part of our code decides which type of values we're dealing with:
<%
strSourceList = Request.Form("txtSourceList") 'value from text box
If Len(strSourceList) = 0 Then 'use files selected in list
'the file list directory they used to get the list of files
'is carried forward from a hidden control in the form:
strListPath = Request.Form("hidListPath")
If InStr(strListPath, ":") Or InStr(strListPath, "\") Then
blnPhysical = True 'its a physical path
If Right(strListPath, 1) <> "\" Then
strListPath = strListPath & "\"
End If
Else 'its a virtual path
blnPhysical = False
If Right(strListPath, 1) <> "/" Then
strListPath = strListPath & "/"
End If
End If
'All the items selected in the list box are comma-delimited
'in a single string from the form. We get this string with:
strFileList = Request.Form("selSourceList")
If Instr(strFileList, ",") Then 'several files selected
'get the first one into strSourceList
strSourceList = strListPath & Left(strFileList, Instr(strFileList, ",") - 1)
Else 'a single file selected
strSourceList = strListPath & strFileList
strFileList = ""
End If
...
Our component expects any files whose names include a space to be enclosed in double quotes (ASCII character 34). We'll do this now for the one file we've already got in strSourceList:
...
If InStr(strSourceList, " ") Then
strSourceList = Chr(34) & strSourceList & Chr(34)
End If
...
If we're dealing with a physical path and have a list of files, we can carry on splitting each one off the original string, wrapping them up in double quotes where necessary, and adding them to the strSourceList string. Each entry in strSourceList needs to be the full path and name of a file we want to include in the zip file, separated from its neighbors by a single space character:
...
If (blnPhysical) And (Len(strFileList)) Then
'physical directory, so we can get the rest of the files
strFileList = Trim(Mid(strFileList, Instr(strFileList, ",") + 1))
strNextFile = strListPath & Left(strFileList, Instr(strFileList, ",") - 1)
If InStr(strNextFile, " ") Then
strNextFile = Chr(34) & strNextFile & Chr(34)
End If
'component expects a source file list delimited with spaces
strSourceList = strSourceList & " " & strNextFile
Do While Instr(strFileList, ",")
strFileList = Trim(Mid(strFileList, Instr(strFileList, ",") + 1))
If Instr(strFileList, ",") Then 'more files to come
strNextFile = strListPath & Left(strFileList, Instr(strFileList, ",") - 1)
Else 'this is the last one in the list
strNextFile = strListPath & strFileList
End If
If InStr(strNextFile, " ") Then
strNextFile = Chr(34) & strNextFile & Chr(34)
End If
strSourceList = strSourceList & " " & strNextFile
Loop
End If
End If
Response.Write "Source file list is <B>" & strSourceList & "</B><P>"
...
Setting the Script Time-Out Value
The last step in the code above is to write the value of the strSourceList into the page, so that the user can see what we think they selected. Before we actually start zipping the files, however, there is one other task. Because the operation may take a while to complete, we'll change the script execution timeout from the default 20 seconds to a higher value:
...
Server.ScriptTimeOut = 600
...
Setting the Component Properties
Finally, we're ready to create the component object and set the properties. Notice how we use the value of the checkboxes (which return "on" if checked or nothing if not checked) to set the Boolean properties of the component. We also need to convert the compression rate value into a number before we try and apply it to the component:
...
Set objSZ = Server.CreateObject("Stonebroom.ServerZip")
objSZ.sourceFileList = strSourceList
objSZ.zipFileName = Request.Form("txtZipFile")
objSZ.virtualTargetRoot = Request.Form("txtTargetRoot")
objSZ.encryptCode = Request.Form("txtEncryptCode")
objSZ.compressionRate = CInt(Request.Form("selCompressRate"))
objSZ.recurseDirs = (Request.Form("chkRecurseDirs") = "on")
objSZ.deleteOriginalFiles = (Request.Form("chkDeleteOriginals") = "on")
...
Checking if the Component is Busy
Everything is now ready to start the zip process, but before we go leaping headlong into it we need to remember that the component does not allow concurrent operations. To get round this, we use the zipIsBusy property that it exposes, and wait up to a couple of minutes for it to become available:
...
If objSZ.zipIsBusy Then
Response.Write "Waiting to access the component ...<P>"
Response.Flush
'force display by the browser with 2 Write/Flush operations
Response.Write ""
Response.Flush
'then wait up to two mins for component to become available
intWaitUntil = (Minute(Now) + 2) Mod 60
Do While (objSZ.zipIsBusy) And (Minute(Now) <> intWaitUntil)
Loop
End If
If objSZ.zipIsBusy Then
Response.Write "Sorry, the component is too busy at present.<P>"
Response.Flush
Else 'OK to do the zip
blnWorked = objSZ.doZip()
End If
Set objSZ = Nothing
%>
And here's the result. Notice that the file is placed in a hyperlink ready to collect. The status bar shows the path to it, complete with the unique subdirectory within the target directory where it was placed:
Figure 3
The remaining section of this page provides the controls for deleting old files and directories that should have already been collected. You can see that the 'Virtual target root' text box already contains the path that we used for this zip file. This saves having to type it in if you want to clear old directories from the same virtual root. It's achieved with the value of the virtualTargetRoot property that we used earlier in the page:
<INPUT TYPE="TEXT" NAME="txtTargetRoot" SIZE=25 VALUE="<% = Request.Form("txtTargetRoot") %>">
Using Other Zip Properties
In the previous example we only typed in values for the required properties of the component, in the controls on the page, leaving the others at their defaults. However we can also provide an encryption code, include any subdirectory contents, and delete the original files:
Figure 4
You can see here that we've entered a physical path to the source files, and included the wildcard file specification *.* (all files). We've also turned on the 'Subdirectory files' and 'Delete originals' options. To prevent you deleting files by accident, a warning dialog pops up when you turn on the 'Delete originals' option. This is done by some client-side JavaScript code written in the page:
<INPUT TYPE="CHECKBOX" NAME=chkDelete onClick="deleteConfirm()">
...
<SCRIPT LANGUAGE="JAVASCRIPT">
<!--
function deleteConfirm() {
if (document.forms[0].elements["chkDeleteOriginals"].checked)
{ var strMesg = 'WARNING: You have elected to delete the '
+ 'original files once compression is complete. '
+ 'Make sure that you do not include files that '
+ 'you do not want to delete.';
alert(strMesg);
}
}
//-->
</SCRIPT>
However, we've also set the Registry value DisableDeleteOriginalFiles to "1" on our server using the supplied SZConfig utility, so when the zip operation takes place an error message is inserted into the page and the originals are not deleted:
Figure 5
This time, you see the nine source files being compressed one by one, and when complete the resulting zip file can be collected. When viewed in a zip file manager, you can see the single subdirectory and its contents that were within the source directory:
Figure 6
And because we provided an encryption key for the encryptCode property, we can’t unzip the files without it:
Figure 7
Deleting Old Unique Download Directories
The final page in our examples is used to remove old downloaded zip files and the uniquely-named directories that they reside in. We only need to worry about two properties for this, one of which defaults to a sensible value if not specified.
The bottom section of the page szdozip.asp that we've just been working with contains the two controls for the virtualTargetRoot and daysOldToDelete properties. These values are passed to the page szdelete.asp when the Next button is clicked, and we use these to set the properties of the component:
This removes the old directories and files, and places a confirmation message in the page. We use the value returned by the deleteOldFiles method to show how many directories were deleted:
<%
...
<B>ServerZip</B> reports that it deleted<B> <% = intFiles %> </B>
unique directories and their contents within the virtual root<B>
<% = Request.Form("txtTargetRoot") %>
...
%>
Here's how it looks in the browser:
Figure 8
At the bottom of the page is a button that returns us to the original szexamp.htm page, so that we can go round again. Rather than use client-side script to load the page, we've made the button a SUBMIT button, and placed it on a form whose ACTION is the ordinary HTML page we want to load. In order for this to work, we have to break with the recommendations of HTML 4 and use METHOD="GET". If we use "POST", the server will report an error:
...
<FORM ACTION="szexamp.htm" METHOD="GET">
<TABLE WIDTH=100%>
<TR>
<TD ALIGN="RIGHT"><INPUT TYPE="SUBMIT" VALUE="Next >"></TD>
</TR>
</TABLE>
</FORM>
...
Summary
And that's it. In this chapter, we've explored the background, requirements, techniques and implementation of a custom Active Server Component, and seen how it can be used with ASP script in our applications. Developing components is not really an Active Server Pages topic, but if you understand how they are built, how they generally work and interact with ASP, and the limitations that they have to work under, it makes it easier to decide when a component is a good solution to your requirements.
We've used Visual Basic in this chapter to build our component, with the intention of making it easy to follow how it works, and what it's doing behind the scenes. Of course, any other language that can create COM-enabled objects, such as C++, Java, Delphi, etc. can be used instead. In our case, where all the real processing is done by a legacy DLL, Visual Basic proves plenty fast enough to act as the interface and the glue that links Active Server Pages with the internal objects inside our component.
Copyright
This text is owned and copyrighted by Wrox Press Limited, UK and USA. No parts may be stored, copied or distributed without the prior permission of the publishers. For more information contact Anthea Elston (antheae@wrox.com), or visit http://www.wrox.com or http://rapid.wrox.co.uk.
Copyright 1998 - Wrox Press Limited, UK and USA.
|