|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionIn your ASP.NET application, you may have a set of static files and directories with many image, script, resource files in them. Especially extensive javascript libraries with plugin support (like TinyMCE) may have hundreds of files on their own. You probably never modify any of these files, unless you upgrade to a newer version of those libraries. When you create a new project, which is using same set of such libraries, you will have to copy whole set of directories and files to new project folder (if you want a simple x-copy deployment you have to...) and end up with thousands of duplicate files polluting your hard-drive. (and if you are like me and using version control systems similar to SVN (a must-have for me), than your job will be easier on repository side, as copying a directory means just creating a link in SVN, but this will not save you from having a new local copy of every file in your working copy). And here comes my solution proposal: Why not keep these libraries (or other resources) in a zip file of their own (as most of them already distributed as zip files) and use them as if that ZIP file is a virtual directory on ASP.NET server? (note: "virtual directory" here is not related to IIS virtual directories) BackgroundFirst of all, why ZIP?
Our requirements for completed system are:
Using the codeOur solution is solely composed of a IHttpHandler implementation (Poligon.ZipFS.ZipFSHandler), which handles requests with URL's that starts with "~/zip.axd". This handler resides in "Poligon.ZipFS.dll" file which can be found in sample archive. Place it in your web site BIN folder, along with Ionic.Utils.Zip.dll and add following lines to your web.config file under "system.web\httpHandlers" section: <configuration>
<system.web>
<httpHandlers>
<add verb="GET,HEAD" path="zip.axd" validate="false"
type="Poligon.ZipFS.ZipFSHandler"/>
</httpHandlers>
</system.web>
</configuration>
<configuration>
<system.web>
<httpModules>
<add name="InternalServerFix" type="Poligon.ZipFS.InternalServerFix" />
</httpModules>
</system.web>
</configuration>
To demonstrate how it works, i created a sample web site with a single page in it (default.aspx). This page contains a link to "index.html" in "beatiful.zip" that resides in same folder. "Beatiful.zip" has a simple web site template with some html, css, gif, jpg files in it. So, when you click the link, Beatiful Day web site template will be launched directly from the zip archive. Let's have a brief look at classes in Poligon.ZipFS library... ZipFileCache represents a single ZIP archive, whose file header entries are cached in memory. It automatically detects changes in underlying ZIP archive (by means of last modification date, and size) and reloads header information when needed. It also keeps a dictionary of [file name --> zip file entry] pairs for fast access to zip file entries by file name. You create an instance of it by providing absolute path of a ZIP file to its constructor. ZipFileCache provides only one remarkable public function which is: public bool ExtractStream(string filePath, Stream stream)
Given name of a file in zip archive, it first checks if ZIP archive is modified since last time headers are loaded, reloads them it if it did, than extracts file contents to given stream. If no file by that name is found in archive, false is returned. ZipFileCache keeps ZIP file open but only locks it for writing during extraction. ZIP archive can be safely replaced when no extraction is in process. ZipFileCache is also thread safe, as it synchronizes access through the cache. I used OneManyResourceLock by Jeffrey Richter (Wintellect) for synchronization, which is simply a lock that allows multiple readers, and a single writer. It is more effective than a mutex (C# lock keyword) when more than one reader can access a resource at same time. In our case, multiple threads can read archive at same time but when archive is modified (which is rare), and needs to be reloaded, other threads should wait till it is done reloading. Please note that filePath should be specified using forward slashes (not backward slashes) like "folder/subfolder/file.txt". ZipFSCache is simply a static collection of ZipFileCache objects, one per each ZIP file. It also allows thread-safe access to this collection. It creates an instance of ZipFileCache object when a ZIP file is first accessed using its ExtractStream function: public static bool ExtractStream(string zipFilePath,
string filePath, Stream outputStream)
It requires a third parameter (first one) in addition to the two in ZipFileCache.ExtractStream method. It is the full path to ZIP archive file (this time using backward slashes...) ZipFSHandler is our IHttpHandler implementation, that handles requests starting with "~/zip.axd". It uses ZipFSCache to extract files from ZIP archives and may also choose to send content directly from physical files, if they are found in a special location (the folder without ".zip" extension, as i explained before). As library code is well documented, you may inspect it to understand how it works. Points of InterestZipFSHandler does it's work nicely, and simply, but may be simpler than it should be. It may sometimes be a security risk to send every ZIP file content without checking if user allowed to access it or not. So, if you have some ZIP files, that you don't want everybody to access, you may have to secure them somehow or add some configuration options to ZipFSHandler. One simple thing i would suggest is to rename ZIP files that you want public to access to .zipfs extension, and modify ZipFSHandler to work with that extension, instead of every ".zip" file. It also keeps ZIP files open, and never close them. I think that when thousands of users requests files from zip files, reopening them for each request would be slower and use more resources than keeping one file handle per each ZIP file open. You may choose to close files after each extraction... History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||