Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Classic ASP Multi-upload Image Gallery

4.77/5 (13 votes)
11 Apr 2014CDDL14 min read 108.8K   5.3K  
A VBScript application to manage image galleries based on the SimpleViewer Flash image gallery.

*** NOTE ***

A vastly superior variant of this application is available here: /Articles/94893/Add-NET-thumbnailing-to-a-classic-ASP-Multi-upload. It requires no DLL registration because it utilizes ASP.NET to render thumbnails. I highly suggest using that version of the gallery. Read both articles to get all the details of how the gallery works but use the ASP.NET version for best results. Thanks! -- LB

Introduction

This is an ASP based image gallery with multi-upload and auto-thumbnail capability. Galleries are displayed using SimpleViewer - a Flash based gallery application. The application utilizes ADO/XML and text files to manage gallery information, so there is no need for database configuration.

Background

This program utilizes the following Open Source applications:

  • SimpleViewer - A Flash based image gallery with flexible configuration options.
  • FreeASPUpload - A VBScript based ASP class for handling file uploads.
  • CXImage Control - A DLL file for image manipulation using COM languages such as VBScript. This DLL must be registered on your IIS server. I'm working on a version that uses .NET for thumbnailing. Look for it in a follow-up article.
  • Prototype.js - AJAX Framework.
  • Scriptaculous - Effects framework based on Prototype.js.

Using the Code

To install the code, drag the gallery folder found in gallery.zip to your IIS folder. Set the images subfolder to enable write access, or you will receive permission denied errors. Browse to your gallery folder and start using the application.

The login and password are "admin" and "admin", respectively. You can change these in the handle_login function found in gallery_app.asp.

Upon login, you'll see a list of existing galleries.

Points of Interest

There is nothing truly outlandish about this code, but there are some interesting areas:

  • Multi-file upload - allows for up to nine files to be uploaded in batch
  • ADO/XML - The use of ADO recordsets and XML to store them as files, versus using a database.
  • An un-utilized feature - A VB.NET based thumbnail generator that if you do some re-coding can get this tool working on hosted sites like godaddy.
  • Drag and drop sorting - The code leverages Scriptaculous' sort feature to enable drag and drop arrangement of the image list.

Multi-File Upload

By combining a hidden IFrame with AJAX requests via prototype.js, the upload page (upload_files.asp) allows up to nine images to be uploaded in batch. As each file is uploaded, an indicator displays adjacent to the current upload. When a file upload is complete, a thumbnail displays adjacent to the form and the next file is processed. When the batch is complete, a button displays prompting to click and upload more files.

ADO/XML

Each gallery utilizes an ADO/XML recordset to manage the file list and follow sort order. Using this technology lets us take advantage of structured storage without having a database setup. Unique IDs are generated for each image using the newid() function found in gallery_app.asp.

An Un-Utilized Feature

There is a file thumbnail.aspx and a corresponding thumbnail.aspx.vb code-behind page that can be utilized to generate thumbnails. The upside of using this page is it does away with the CXImage DLL, which is beneficial in hosting environments that will not allow for controls to be registered. The downside is you'll have to do some re-coding in order to leverage the .NET based thumbnailer. I hope to address this change in a future article.

Drag and Drop Sorting

The "Arrange" link leads to a page that lets you drag and drop gallery images to re-arrange their position in the gallery view. This feature utilizes Scriptaculous "sortables" to accomplish the task.

The Code Explained

The bulk of this application's code can be found in gallery_app.asp. Each page is a stub that holds the HTML content, and makes calls to functions within gallery_app.asp. Several pages also utilize AJAX calls to "functional pages" to perform operations behind the scenes and provide a more engaging user experience. Let's run through each page, I'll explain the functionality, then dive into how the code works and where the AJAX calls are made. I'll also explain what pages are called. First, a little background.

Libraries and Naming Conventions

I use prototype.js for my AJAX framework and scriptaculous.js for effects. I use a "code behind" convention for JavaScript to separate JavaScript code from the HTML. You'll find the JavaScript code for a given page named as "pagename.js". For example, if the page is named upload_files.asp, you'll find its JavaScript library named upload_files.js.

The main code library for the server-side code is gallery_app.asp.

AJAX Calling Convention

You'll see the same basic structure appear on pages where I use AJAX calls. Using Prototype's Ajax.Request method, a basic template for the method looks like this:

JavaScript
new Ajax.Request('action_page.asp?gallery_id=' + id, {
    method: 'post', // use post method
    postBody: $('form_name').serialize(),
    onSuccess: function(transport){
        // every action page returns HTML that is inserted
        // directly into the results container. 
        $('results_container').innerHTML=transport.responseText;
    }, // end on success
    onFailure: function(transport) {
        // Append an appropriate error message to the results container
        var err = "<br/>Error creating gallery"+transport.responseText;
        $('results_container').innerHTML=$('results_container').innerHTML + err;
    } // end on fail
}); // end ajax request

'action_page.asp' is the name of the page that performs a designated action. In most cases, the action form re-renders the part of the page being acted upon to reflect the results. form_name is the ID of the form that contains data to be posted to action_page.asp. 'results_container' is the ID of a div tag that is used to display the results of the request; in most cases, this is the container that holds the calling form. On successful calls, the contents of this container are replaced with the rendered content passed back in the AJAX response.

Here's a sample of the toggle gallery function used to make galleries show/hide:

JavaScript
function toggleGallery(id) { // all ajax calls pass the gallery id in the querystring
    new Ajax.Request('toggle_gallery.asp?gallery_id=' + id, {
        method: 'post', // all actions use the post method
        onSuccess: function(transport){ // action pages return content ready for use
            $("gallery_list").innerHTML=transport.responseText;
        }, // end on success
        onFailure: function(transport) {
            $("gallery_list").innerHTML=$("gallery_list").innerHTML + 
                    "<br/>Error creating gallery"+transport.responseText;
        } // end on fail
    }); // ajax request
    
}

In the above example, I'm simply passing the gallery ID; some calls also include data from a form. If I am posting data from a form, you'll also see the following code line below the method 'post'.

JavaScript
postBody: $('form_name').serialize(),

Where 'form_name' is the ID of the HTML form that is being passed to the page.

OK, now let's hit the content pages.

gallery.asp

This is the main display page for the gallery. It calls the Flash "SimpleViewer.swf" file. It also passes the gallery ID to the page gallery.xml.asp which assembles an XML document that SimpleViewer uses to construct the list of images. Along the top, you'll see a menu of your gallery titles and an admin link. Clicking the admin link brings you to the gallery list page.

gallery_list.asp

Image 1

This page shows the list of your existing galleries; from here you can: toggle the show hide status of a gallery, create a new gallery, access Info, Upload, Images (captions), and view the gallery via gallery.asp.

"Create a new gallery" makes an AJAX call to the action page create_gallery.asp which does four things: get a new ID [id] by incrementing the gallery ID counter found in images/id.txt, create a new gallery info file [id].txt, a new gallery table to hold the image list and sort order [id].xml, and finally a gallery images and thumbnail folder [id] and [id]/thumb. [id] is replaced with the new ID. See the function "CreateGallery" in gallery_app.asp for more details.

The "Hide/Show" buttons in the rightmost columns make AJAX calls to toggle_gallery.asp which adds/removes the gallery ID from the "hidden" list found in images/config.txt. The gallery ID is passed in the querystring.

In the Tools column, you'll find four links. Info lets you edit/update the gallery title. Upload lets you upload new images to the gallery. Images lets you mange images (add/remove) as well as edit captions. Finally, View lets you see your changes in SimpleViewer.

edit_gallery.asp

Image 2

Edit gallery info lets you change the gallery title and description. The description is not used anywhere at this point. This is a pretty no frills form. When you click Save, an AJAX call is made to the action page save_gallery.asp which makes a call to the function SaveGallery() found in gallery_app.asp. The information on this page is saved to a text file "images/gallery_id.txt", where gallery_id is the ID number of the passed in querystring (parameter: gallery_id). The ID is passed automatically when you click the "info" link from the gallery_list.asp page.

upload_files.asp

Image 3

This is where this whole project started. I wanted to find a way to upload multiple files using ASP, but at the same time provide some feedback as to the uploading process. Ideally, this would come in the form of a progress bar for each file; however, I couldn't figure out a way to do that. So I did the next best thing: add an indicator image (a spinner if you like) indicating the current upload.

To upload files, click the "Browse..." button and locate the desired image from your hard drive (depending on your browser, the button might say "Choose..."). You can upload up to nine images in one batch. When you have selected all the images you wish to upload, click the "Start uploading images" button.

Upload Progress

Image 4

As each file uploads, the spinner indicates the current upload (look at the third image). When an upload completes, a thumbnail is placed adjacent the browse (or Choose) button and a larger thumbnail along with the image size is placed under the title "Last Completed Upload". The process continues for each image until the list is complete.

Upload Complete

Image 5

When the last image is uploaded, a button appears that prompts you to upload more images if you wish. The process can be repeated as many times as you like.

So what happens when the "Start uploading images" button is clicked? A JavaScript function "uploadClick();" is invoked (found in upload_files.js). This function iterates through each form, setting it to disabled, and also setting the thumbnail image to blank. Disabling the forms prevents the user from interacting with the form during the upload process.

JavaScript
var upload_counter = 0
// Handle Upload chain
function uploadClick() {
  $("startupload").fade();
  upload_counter = 1;
  for (x=1;x<10;x++) {
    $("uploaded_image0"+x).src="blank.gif"; // Blank thumbnail image
    $("file_upload_form0" + x).disable();   // Disable form.
  } 
  handleForm();
}

This works because each image is stored in its own HTML form. Each form contains a file upload input box, a place holder for the upload thumbnail, and a placeholder for the progress indicator (spinner). Each form and all of its elements are named with a 00, 01, 02 etc... extension to make accessing them using an iterator easy. Also, notice the "target" attribute of the form is set to 'upload_target'; more on that later. Lastly, notice that the "action" attribute is upload.asp; this is the action page that processes uploads.

JavaScript
<form id='file_upload_form02' target='upload_target' method='post' 
            enctype='multipart/form-data' action='upload.asp?gallery_id=1'>
Image: 
   <input name='file' id='file02' size='40' type='file' />
   <img id='uploaded_image02' width='24' height='24' src='blank.gif'/>
   <img src='indicator.gif' id='upload_indicator02' style='display: none;' />
<br />
</form>

At the end of uploadClick, the function handleForm() is called (also located in upload_files.js). Handleform increments the counter for a file, turns on the appropriate indicator, and submits the form by enabling it, submitting, then disabling it again.

JavaScript
// handles a form update, only populated forms are submitted
function handleForm() {

  while ( ($("file0" + upload_counter )!=null) 
   && ($("file0" + upload_counter ).value != "" ) 
   && (upload_counter<10) ) 

      upload_counter++; // Increment the counter
 
      if ( upload_counter<=9 ) {
          $("upload_indicator0" + upload_counter).appear(); // Turn on the indicator
          $("file_upload_form0" + upload_counter).enable(); // enable the form
          $("file_upload_form0" + upload_counter).submit(); // Submit the form
          $("file_upload_form0" + upload_counter).disable(); // disable the form
      } else {
          batchComplete(); // if all files are processed call batch complete
  }

}

Each form submits to a hidden IFrame (the same IFrame) located below the list of forms. This IFrame acts as the target for the upload form. Here is the IFrame source:

HTML
<iframe 
   id='upload_target' 
   name='upload_target' 
   src='about:blank' 
   style='width:0;height:0;border:0px solid #fff;' >
</iframe>

Since the target of each form is set to 'upload_target' and our IFrame is named 'upload_target', the submit request is sent to the IFrame rather than the whole page. This allows each file to be uploaded in sequence while not disturbing the main HTML page (upload_files.asp). The IFrame also serves as the return mechanism when the file is complete.

When every file has been uploaded, batchComplete is called, which simply turns on the button, prompting to execute another batch.

upload.asp

Before I continue with the content pages, I think it is a good point to go into the details on the uploads action page. I won't detail all the action pages as it is easy enough to describe their functionality in a sentence or two. Upload.asp is a different beast, so I'll single it out here.

Upload.asp is an action page that incorporates FreeASPUpload to enable file uploading. The neat thing is that it is free and Open Source, so you can tweak and alter its behavior to your heart's desire. It is also free, so you can use it without registering DLLs. This page uses another Open Source project (CXImageControl) to generate thumbnails; unfortunately, this control requires server side registration. You can easily adapt the thumbnail function to work with whatever image control they provide on the server. I'm also working on a follow up article (provided this one provokes enough interest) that will utilize a single .NET page to do thumbnails thus making this gallery "component free" and easy to run on hosting services that are not "DLL registration friendly".

Here is a rundown of how uploads.asp works ([gallery_id] in the bullets below is a placeholder for whatever the real gallery ID is as passed in the querystring).

  • Create and call FreeASPUploads to load the image file.
  • Call the FreeASPUploads object to save the files to images/[gallery_id]/.
  • Create a timestamp prefix (four digit year + '.' + day of year + '.' + timer).
  • Rename each file with the timestamp to prevent naming conflicts.
  • Generate thumbnail images in the following formats: 32x32, 64x64, 96x96, 120x120, 240x240, 480x480, 640x640, 800x800, and 960x960. Thumbnails are placed in images/[gallery_id]/thumb/ and are named filename.WxH.jpg.
  • The file is registered with the gallery by adding it to the images/[gallery_id].xml file; this is done using ADO. I'll explain that shortly.

The ProcessUpload function is a modified version of ProcessUpload that is included with the upload_test.asp file in the FreeASPUpload package. The code snippet is rather long so I won't include it here. I will go over the parts I changed and get into the functions called by ProcessUpload.

The Date Stamp

The date stamp is used to give each file a unique name to avoid file name conflicts.

VBScript
sStamp = year(now()) & "." & datepart( "y", now() ) & "." & CLng( Timer ) & "."

The uploaded file starts off with the same name as it had on your hard drive so we have to rename it using the MoveFile method of Scripting.FilesystemObject. This is shown in the code below as "fso.". This code snippet can be found starting at line 63 of upload.asp.

VBScript
strFile = Upload.UploadedFiles(fileKey).FileName ' The file as it is named on your hdd
sOldFile = uploadsDirVar & "\" & strFile ' the path to where the file was uploaded
strFile = sStamp & strFile  ' now set the file to the new time stamp prefixed name
strFileName = uploadsDirVar & "\" & strFile ' create a path to the new file
call fso.MoveFile( sOldFile, strFileName ) ' Call fso to move the file

After the file is renamed, it is registered in the gallery using the following code line (line 74 of upload.asp):

VBScript
AddGalleryImage( strFile )

AddGalleryImage loads the current gallery file (images/gallery_id.xml) by making a call to OpenTable(filepath, rs) with the full path to the file and a recordset object, appending the new file to the recordset, and saving the recordset as XML using a call to SaveTable(filepath, rs) which writes the updated data back to the XML file. Here are three functions along with a fourth that creates a new empty gallery table; these functions are found in gallery_app.asp:

VBScript
''**********************************************************************
'' Function: AddGalleryImage
'' Version: 1  
'' Date: 2009/07/11
'' Author: Larry Boeldt
'' Description: Adds a new image to the current gallery. 
''**********************************************************************
function AddGalleryImage(sFileName)
    dim path
    dim rs
    dim lOrder
    path = server.mappath(".") & "\images\" & lGalleryID & ".xml"
    
    OpenTable path, rs
    
    rs.addnew
    rs("id")=newid()
    rs("dateadded")=now()
    rs("filename")=sFileName
    rs("order")= year(now)*10000+month(now)*100+day(now)
    rs.update
    SaveTable path, rs
end function

''**********************************************************************
'' Function: SaveTable
'' Version: 1  
'' Date: 2009/07/11
'' Author: Larry Boeldt
'' Description:
''**********************************************************************
function SaveTable(FullPath, rs)
    DeleteFileIfExists FullPath
    
    if not rs.bof then
        rs.movefirst
    end if
    
    rs.Save FullPath, 1 '' adPersistXML = 1
end function

''**********************************************************************
'' Function: OpenTable
'' Version: 1  
'' Date: 2009/07/11
'' Author: Larry Boeldt
'' Description: Open an ADO XML table
''**********************************************************************
function OpenTable(FullPath, rs)
    set rs=CreateObject("ADODB.Recordset")
    rs.Open FullPath, "Provider=MSPersist", , , &H0200 ''adCmdTableDirect 
end function

''**********************************************************************
'' Function: GalleryTable
'' Version: 1
'' Date: 2010-07-09
'' Author: Larry Boeldt
'' Description:
'' Create the gallery xml table file to contain a list of files
''**********************************************************************
function CreateGalleryTable(FullPath)
    dim rs
    set rs = CreateObject("ADODB.Recordset")
    rs.CursorLocation  =  3 '' adUseClient
    
    rs.Fields.Append "ID", 200, 40  '' adVarChar = 200
    rs.Fields.Append "FileName", 200, 240
    rs.Fields.Append "Caption", 200, 4096 
    rs.Fields.Append "DateAdded", 7 '' adDate = 7
    rs.Fields.Append "Size", 3 '' adInteger = 3
    rs.Fields.Append "Order", 3 ''
    rs.Open 
    SaveTable FullPath, rs
        
    set rs = nothing
end function

Finally, a series of calls is made to the Thumbnail function to generate each thumbnail image. Thumbnail takes these parameters: Filename, Width, Height, KeepAspectRatio, and JpegCompressionRatio. If KeepAspectRatio is 0, the image will be stretched or shrunk to fit the exact dimensions; if it is 1, the image will be scaled to fit the width dimension passed.

VBScript
Call ThumbNail(strFileName, 32, 32,1,80)

Here is the full ThumbNail function. You'll see where I used CXImageControl to open the original file, resize it, and save it to the thumb folder.

Vbcript
function ThumbNail(strFileName,lWidth,lHeight,lKeepRatio,lCompression)
    '    response.Write "Call to Thumbnail"
    dim fso: set fso = createobject("scripting.filesystemobject")
    dim lH: lH=lHeight
    dim lW: lW=lWidth
    dim lRatio: lRatio = lKeepRatio
    dim strSitePath
    ''response.write strFileName
    strSitePath = fso.GetFile(strFileName).ParentFolder
    dim strHTML
    dim strSRC 
    dim strOnC ' OnClick
    dim strImgPath
    dim strThumbPath
    dim strURL
    dim bExists
    dim strThumbName
 
    strImgPath = strFileName
    rem convert to cx image
    dim pWidth
    dim pHeight
    dim FileName
    dim sRoot
    dim sFile
    dim bStretch
    dim widthTh
    dim heightTh
    dim widthOrig 
    dim heightOrig 
    dim objCxImage
    dim Quality
    dim strResult
    rem use cximage
    bStretch = (lKeepRatio = 0)
    Quality = lCompression
    sRoot = strSitePath 
    sFile = fso.getFile( strFileName ).name
    FileName = strFileName
    pWidth = lW
    pHeight = lH
    ThumbName = uploadsDirVar & "thumb\" & sFile & "." & _
                pWidth & "x" & pHeight & ".jpg"
    ' Create COM CxImage wrapper object
    Set objCxImage = CreateObject("CxImageATL.CxImage")
    Call objCxImage.Load(FileName,GetFileType(FileName))
    Call objCxImage.IncreaseBpp(24)
    ' determine thumbnail size and resample original image data
    If bStretch Then ' stretch to fit 
        widthTh = Width
        heightTh = Height
    Else ' retain aspect ratio
        widthOrig = CDbl(objCxImage.GetWidth())
        heightOrig = CDbl(objCxImage.GetHeight())
        fx = widthOrig/pWidth
        fy = heightOrig/pHeight 'subsample factors
        ' must fit in thumbnail size
        If fx>fy Then f=fx Else f=fy  ' Max(fx,fy)
        If f<1 Then f=1
        widthTh = Int(widthOrig/f)
        heightTh = Int(heightOrig/f)
    End If
    objCxImage.SetJpegQuality( Quality )
    Call objCxImage.Resample(widthTh,heightTh,2)
    call objCxImage.Save(thumbname,GetFileType("jpg"))   
    Call objCxImage.Destroy()  
    set objCxImage = nothing
    rem end convert to cx image
    Thumbnail = ThumbName
  
end function 

OK, now that we see how upload.asp works, let's finish off with a quick roundup of the remaining pages.

captions.asp

Image 6

Captions.asp is where you manage image captions (the text that appears with the image) and remove images from the gallery; this page is accessed anywhere you see an "images" link. The onChange event of each caption box triggers the saveCaptions function found in captions.js. This function calls the action page savecaption.asp which takes the submitted data and saves it to images/[gallery_id]/[imagename].txt.

JavaScript
function saveCaption( obj ) {
    var data = obj.innerText
    var tag = obj.id
    
    var file = $("file." + tag).value // get the value of the caption textarea
    
    if (data =="") data = " "; // ensures we don't have a zero length file error
    
    // we make a call to savecaption.asp (always passing the gallery_id in QS    
    $("indicator."+tag).show();
    new Ajax.Request( "savecaption.asp?gallery_id=" + gallery_id + "&file=" + file, {
        method: 'post',
        postBody: data,
        onSuccess: function(transport) {
            $("indicator."+tag).fade();
        }
    });
}

The textarea code that triggers this call looks like this:

HTML
<textarea 
    onchange='saveCaption(this);' 
    style='margin: 6px;' rows=4 cols=40 
    name='caption' 
    id='IMG_3258.JPG'></textarea>

Notice the onChange event is set to 'saveCaption(this)' so all you have to do is change a caption and it is automatically saved. No need to click Submit.

When the Remove button is clicked, the function removeImage is invoked, which removes the image by calling the action page removeimage.asp. It works similar to the call to saveCaption, but fades the form onSuccess. I'll leave you to study that function on the site.

sort.asp

Sort.asp is accessed anywhere you see an "arrange" link. It brings up a list of the files in a grid as they will be displayed in the SimpleViewer Flash. You simply drag and drop the images where you want them and their order is maintained. The sort list is generated by the function ShowSortList found in gallery_app.asp.

VBScript
''**********************************************************************
'' Function: ShowSortList
'' Version: 1  
'' Date: 2009/07/11
'' Author: Larry Boeldt
'' Description: Shows the sorting list form.
''**********************************************************************
function ShowSortList
    dim fso
    dim spath
    dim fil
    dim sExt
    dim sFile
    dim rs
    
    set fso = createobject("scripting.filesystemobject")
    sPath = server.mappath(".") & "\images\" & lGalleryID & "\"
    sFile = server.mappath(".") & "\images\" & lGalleryID & ".xml"
    
    OpenTable sFile, rs
    
    rs.sort = "order asc" ' sort the recordset in the desired sort order
    
    do until rs.eof
        response.write "<li id='item_" & rs(0).value & "'>"
        response.write "<img src='images/" & lGalleryID & "/thumb/" & _
                       rs(1).value & ".96x96.jpg'/>  " 
        'response.write rs(0).value  
        response.write "</li>"
        rs.movenext
    loop
end function

Near the bottom of sort.asp, you'll see the following JavaScript which creates the sortable page and sets the target to the sort.events.asp action page.

JavaScript
<script language="javascript">
Sortable.create("sort_list",
{
    onUpdate: function()
    {
        new Ajax.Request("sort.events.asp?gallery_id=<%=lGalleryID%>",
        {
            method: "post",
            parameters: { data: Sortable.serialize("sort_list") }
        }
        );
    }
}
);
</script>

sort.events.asp receives as data the list of images in their new order. It reads the list of sorted items in order and sets their "order" value in the gallery to their new ordinal value. This uses the OpenTable and SaveTable methods. See the code below to see how I did the sort.

VBScript
handle_sort_list
function handle_sort_list
    ' response.write request.form & request.querystring
    ' data=list_to_sort[]=&list_to_sort[]=&list_to_sort[]=&list_to_sort[]=<? row.id; ?>
    dim sData
    dim aryData
    dim sSQL
    dim iX
    dim list
    dim sPath
    dim sFile
    dim rs
    dim str

    ' lGalleryID is a global variable set using request.querystring("gallery_id").
    sPath = server.mappath(".") & "\images\" & lGalleryID & "\"
    sFile = server.mappath(".") & "\images\" & lGalleryID & ".xml"
    sData = URLDecode( request.form("data") )
    if instr( sData, "&" ) = 0 then exit function
    
    sData = replace( sData, "sort_list[]=", "", 1, -1, 1 )
    aryData = split( sData, "&" )
    
    str = str & "GalleryID: " & lGalleryID & vbcrlf 
    str = str & vbcrlf & vbcrlf
    str = str & sData & vbcrlf & vbcrlf 
    set list = createobject("scripting.dictionary")
    
    str = str & "upper bound: " & ubound( aryData ) & vbcrlf & vbcrlf
    ' Read each file in order into a dictionary object 
    ' where filename is key and order is value
    for iX = 0 to ubound( aryData ) 
        list.add aryData( iX ), iX
        str = str & aryData( iX ) & vbtab & ix & vbcrlf
    next
    str = str & vbcrlf & vbcrlf
    ' Now open the gallery ado recordset
    OpenTable sFile, rs
    ' iterate the file list
    do until rs.eof
        str = str & rs("id") & vbtab & list( rs("id").value ) & vbcrlf
        ' set the order of the file by getting the value from the dictionary object


        rs("order")=list( rs("id").value )
        rs.update ' save changes
        rs.movenext
    loop
    str = str & vbcrlf 
    SaveTable sFile, rs ' Call Save Table to update the gallery list
    
end function

I believe that covers the main functionality of the application. Let me know if you have any questions. Thanks for reading, and enjoy tinkering with the image gallery.

Update: I posted part II of this article here: Add .NET thumbnailing to a classic ASP multi-upload image gallery.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)