|
Introduction
My business clients often request a way to display a list of files located on a shared file server. These files could be CAD drawings, Word documents, or image files. My objectives when approaching these requests are not only to have an intuitive user interface and robust functionality for my end product, but also to include the file lists be user maintained and not kept the files locally on my Web server. The end product should be flexible enough to allow the code to be used by other applications. This may seem simple at the outset, but achieving all of these objectives is not as easy as one might think.
The Plan of Attack
My first attempt involved using the FileSystemObject inside of an ASP page to retrieve a list of files from a directory name. This works fine if the files are located directly on the Web server, but it stops short of being able to access a remote share point. The FileSystemObject will not accept a virtual path and cannot obtain the network access necessary to resolve a UNC name.
Back to the Drawing Board
The problem was I could not use a UNC name. I figured that the FileSystemObject object itself was holding me back, so my second attempt was to build something that would accept a UNC name, thus avoiding the problem instead of fixing it. So I built a shiny new, active x DLL!
My COM object had three main functions inside of it. GetFileList would return a comma-delimited list of files that could be filtered by using the DIR function in VB. And for good measure I threw in the ability to check and modify attributes of files. The TypeConstant arguments allow a developer to call for specific types of objects to be returned in file lists. VBFolder, for example, would return only folder objects in the path, which matched the format of the strNameFilter property. Similarly, the AttribConstant arguments will check for or set standard file attributes. VBReadOnly, for example, would check or set the read-only attribute of a file. This GetFileAttribute function uses this constant an interesting property of the GetAttr function inside of VB, to determine which attributes are set. The function uses the And operator to perform a bitwise comparison of the value returned by the GetAttr function and the value of the individual file attribute wanted. If the result is not zero, that attribute is set for the named file. For example, the return value of the following And expression is zero if the Archive attribute is not set:
Result = GetAttr(FName) And vbArchive
A nonzero value is returned if the Archive attribute is set.
Some of the class code is shown here:
Private Function GetFileList(strPath As String, TypeConstant As Integer) As Boolean
On Error GoTo GetFileList_Error
Dim intFileInex As Integer
Dim intUbnd As Integer
Dim strNameHolder As String
GetFileList = False
strFileList = ""
strFileList = Dir(strPath & "\" & strNameFilter, TypeConstant)
Do Until True = False
strNameHolder = Dir()
If strNameHolder = "" Then
Exit Do
ElseIf strNameHolder = "." Then
ElseIf strNameHolder = ".." Then
Else
strFileList = strFileList & "|" & strNameHolder
End If
Loop
GetFileList = True
Exit Function
GetFileList_Error:
GetFileList = False
End Function
Private Function GetFileAttribute(AttribConstant As Integer) As Boolean
GetFileAttribute = False
If GetFileList(strFilePath, vbDirectory) = False Then
GetFileAttribute = False
Exit Function
End If
GetFileAttribute = GetAttr(strFilePath & "\" & strNameFilter) And AttribConstant
End Function
Private Function SetFileAttribute(AttribConstant As Integer) As Boolean
On Error GoTo SetFileAttribute_Error
SetFileAttribute = False
If GetFileList(strFilePath, vbDirectory) = False Then
SetFileAttribute = False
Exit Function
End If
SetAttr strFilePath & "\" & strNameFilter, AttribConstant
SetFileAttribute = True
Exit Function
SetFileAttribute_Error:
SetFileAttribute = False
End Function
Much documentation has been written specifying that it is incorrect to use properties in COM objects. This statement does not take into account that different objects can be written for different purposes. An object can use properties and consistently function correctly if the object is created and destroyed in one section of code. This approach does limit the use of the object, but it also simplifies some coding, and a good developer can still maintain an appropriate level of abstractness.
All of this code worked fine on my machine when I was running inside the VB environment using an object reference. I confidently went back to the Web server and registered the DLL with much aplomb. My air of invulnerability was shattered, however, with the resulting flood of error messages. Discovering the exact reasons for this took several hours and many emails back and forth between myself and my personal pool of ASP and IIS gurus. This is because a Web server using challenge-response authentication will actually impersonate a user. An impersonated account can resolve what access the person has to given files, but the impersonation will still not have the necessary network permissions to resolve using a UNC name. An additional fact came up. IIS and the WWW Service cannot be configured to run under a custom ID with a custom set of permissions. I also tried rigging this in an effort to give the service the necessary network access.
Back to School
One of my biggest daily battles is information overload. There is so much information about technology available out there in books, in classes, on the Web, and in many other resources that I sometimes think I could spend a number of years just learning about what to use and how to use it and never actually do anything. Realizing that my college days are behind me, cracking books and attending classes tends to be the option less used. One of the biggest challenges in our field is determining what information is “need to know.” Too much information will make you a tinkering geek who gets little accomplished, while too little will make you a dangerous hack. I typically try to play and experiment with doing something with technology I understood before delving into deep research about something unfamiliar.
At this point in my travails I was perplexed. I was faced with compromises my client did not want to make. I could put the files on the Web server as a template to draw a list from and let the users have access to the real thing. This approach invites a lot of maintenance nightmares, not to mention all that wasted disk space for the sole purpose of generating a dynamic list. Who would ensure the file lists would be in sync? Our Web administrator had too many tasks to lose sleep over to worry about copying files back and forth. An old rule of thumb comes to mind; “If there are two supposedly identical sets of data or files, they are usually both wrong.” Giving multiple business users “change rights” to the Web server and managing the NT and IIS security was not an attractive option either. Given these problems and possible compromises it became clear that I had to learn more about the NT/IIS security model and how to trick it into doing what I wanted. This is the point where MTS wandered into the less-traveled lands of “my need to know.”
With the help of some Microsoft insiders I learned some new stuff about MTS. Every package in MTS can run under a specific user ID and password, just like an NT service. These access permissions take priority no matter who or what is calling the object. My solution going forward was simple and it made use of the work I had already done. I took my DLL and had it set up as a new “package” inside MTS. I configured it to run on a specific account whose password never changes and all was right with the world. This last point about using an account with a static password is important to note because it will prevent that account from getting locked out of the system in the future.
The Final Product
Armed with my fully functional COM object I proceeded to code my ASP page to display my file lists. Here is a sample of some code that displays a list of folders. Clicking on the folders will give way to a list of jpg files inside the folders. The folders are pushed through a state filter on an earlier page which uses the NameFilter property to return only folders with the appropriate state prefix. I have since put this ability to use in other applications that need folder lists by divisions, departments, and other standard abbreviations.
Dim strState
Dim strFileFolder
Dim strFile
strState = Request.QueryString("strState")
strFileFolder = Request.QueryString("strFileFolder")
If (Not IsEmpty(Request.QueryString("strFileFolder"))) Then
ObjectPathString = "\\bpsatl3\BPTCHSVC\bpirWeb\admin\sitepix\" &_
Request.QueryString("strFileFolder")
LinkPathString = "//bpsatl3/BPTCHSVC/bpirWeb/admin/sitepix/" &_
Request.QueryString("strFileFolder")
Set FileListObj = Server.CreateObject("FileList.clsFileList")
FileListObj.FilePath = ObjectPathString
FileListObj.NameFilter = "*.jpg"
liststring = FileListObj.WriteFileList
if not liststring = "" then
Dim iU2
Dim sFileArray2
sFileArray2 = split(liststring, "|")
iU2 = UBound(sFileArray2)%%gt;
%lt;%if iU2 %gt;=0 then
for i = 0 to iU2
sName = sFileArray2(i)
strFile = (Request.QueryString("strFileFolder")& "/" & sName)%%gt;
%lt;td width='100' height='40' valign='top'%gt;
%lt;a href='sitepix.asp?Section=Admin&Page=sitepix&Level=0&strState=%lt;%= strState%%gt;
&strFileFolder=%lt;%= strFileFolder %%gt;&strFile=%lt;%= strFile%%gt;&CurFile=%lt;%=i%%gt;
&FileString=%lt;%=liststring%%gt;'%gt;
%lt;img src='..\images\film.gif' hspace=15 border=0 align=center%gt;%lt;br%gt;
%lt;font size=2 face= 'Tahoma,TimesNewRoman,Arial'%gt;%lt;b%gt;
%lt;%response.write sName%%gt;
%lt;/b%gt;%lt;/font%gt;%lt;/a%gt;
%lt;%if (i + 1)Mod 4 = 0 then%%gt;
%lt;/tr%gt;%lt;tr%gt;
%lt;%end if
Next
end if
else%%gt;
%lt;font size=2 face= 'Tahoma,TimesNewRoman,Arial'%gt;%lt;b%gt;
No Files Available for Listing
%lt;/b%gt;%lt;/font%gt;
%lt;%end if
set FileList Obj = nothing%%gt;
%lt;%ElseIf (Not IsEmpty(Request.QueryString("strState"))) then
ObjectPathString = "\\bpsatl3\BPTCHSVC\bpirWeb\admin\sitepix\"
LinkPathString = "//bpsatl3/BPTCHSVC/bpirWeb/admin/sitepix/"
Set FileListObj = Server.CreateObject("FileList.clsFileList")
FileListObj.FilePath = ObjectPathString
FileListObj.NameFilter = Request.QueryString("strState") & "*.*"
liststring = FileListObj.WriteFolderList
if not liststring = "" then
Dim sFileArray
Dim iU
sFileArray = split(liststring, "|")
iU = UBound(sFileArray)
if iU %gt;=0 then
for i = 0 to iU
strFldr = sFileArray(i) %%gt;
%lt;td width='150' height='40' valign='top'%gt;
%lt;a href='sitepix.asp?Section=Admin&Page=sitepix&Level=0&strState=%lt;%= strState%%gt;
&strFileFolder=%lt;%= strFldr%%gt;'%gt;
%lt;img src='..\images\pixfolder.gif' hspace=15 border=0 align=center%gt;%lt;br%gt;
%lt;font size=2 face= Tahoma,TimesNewRoman,Arial'%gt;%lt;b%gt;
%lt;%response.write strFldr %%gt;
%lt;/b%gt;%lt;/font%gt;%lt;/a%gt;%lt;/td%gt;
%lt;%if (i + 1)Mod 4 = 0 then%%gt;
%lt;/tr%gt;%lt;tr%gt;
%lt;%end if
Next
end if
else%%gt;
%lt;font size=2 face= 'Tahoma,TimesNewRoman,Arial'%gt;%lt;b%gt;
No Files Available for Listing
%lt;/b%gt;%lt;/font%gt;
%lt;%end if
set FldrListObj = nothing
End If%%gt;
My objectives at the beginning of this project were to:
- Present an intuitive user interface. - Achieved! My user interface can use the different filters and constants transparently, and users can drill down into lists with a true point-and-click interface that requires little training.
- Provide robust functionality. - Achieved! My object also provides ways to check and set file attributes.
- Have the file lists be user maintained. - Achieved! Not one user has come to us asking to post files to their site.
- Having the files remain at their original location. - Achieved! The users’ list can grow ad infinitum for all I care. That’s the network administrator’s problem (hee hee!).
- Provide a flexible architecture. - Achieved! We now use this object in several applications. The developer provides a path, a filter does a little of that cut-and-paste shuffle, and the application is off and running!
The big benefit of using an approach like this is that the file list displayed by the site is now entirely maintained by the site’s customers. The users push files to their standard locations on the network, as they are already doing, and the site dynamically changes with each hit. While this is common for ASP pages drawing data from a database, this cannot happen through straight ASP and with a FileSystemObject trying to list files on other servers.
Have fun!
|