Many of the workers I support in my company spend part of each day capturing digital photographs in the field. The workers move the images from local flash cards to a NAS device. I have two problems. First, my users find it tedious to copy their files to the network every few days. Second, they don't use consistent names for the folders they copy the files to. Each field worker has a parent directory that identifies the name of the user moving the files. Below that level, each user creates a subdirectory each time he or she moves files to the network. However, the subdirectory names differ from user to user and even from one upload to the next. I'm looking for a script that will automate and simplify the upload process and that uses some identifying file characteristics to uniquely and consistently name the subdirectory for each set of files each user moves. Can you help me find such a solution?

This question was particularly interesting to me because for months, I've been promising my wife that I'll build a script to quickly and easily move image and movie files from a flash drive to unique folders on a shared network. When I asked my colleagues whether they thought such a script would be useful, the universal response was, "Man, when you get that thing written, send it to me!" But it took this reader's question to get me going. I carefully framed my answer and the resulting script, FilesUp load.wsf, to address the submitted question. You can download the complete-script from the Windows Scripting-Solutions Web site. See page 1 for download information. I think you'll find this script useful beyond the specific scenario the question describes.

The goal is to dynamically create a subdirectory for each user. The directory must be uniquely named based on some property of the files targeted for upload. I used the file creation date property associated with all files as the identifying characteristic for folder naming. I figured that in some cases, all files will be created and uploaded on the same day and in other cases, users will take a few days or even weeks worth of photos before uploading them to the network. Therefore, the solution I scripted moves files of a specific file type from one location to another. The source and target locations can be any location that the computer can access, such as a removable disk or a Universal Naming Convention (UNC) path. The source location defines the exact location of the files on the flash drive. Typically, cameras store images in one directory, so the source represents one folder location. The target folder is actually the parent directory that the script targets for creating a uniquely named subdirectory to store the moved files. Although the reader's question is about how to move image files, I decided to make the script more extensible so that you can move files of any type from one location to another.

When I first considered answering this question, I thought the resulting script would be short and simple. To my surprise, the script was more complex than I expected. The script length grew as I dealt with limitations associated with the FileSystemObject object—arguably, the best Windows Script Host (WSH) object for moving files from one location to another. Using the CIM_DataFile class in Windows Management Instrumentation (WMI) is another option, but I found that technique less flexible for this specific purpose and slower performing than the FileSystemObject object. You could choose from many other scripting and coding options to complete this task. For example, you could batch-script a solution and rely on RoboCopy to perform the file management tasks. Or you could code a solution as a .NET console command or use the new Microsoft Shell (MSH) to complete the task. I used WSH for this solution because the scripting engine is ubiquitous and well understood.

The script file format is Windows Script File (.wsf), which takes advantage of the self-documenting < runtime> element and integrated Help features that .wsf files provide through the ShowUsage method. Files-Upload.wsf takes three parameters: the source folder, the target folder, and the file extension. For example, to move .jpg files from the h:\photos directory on a flash disk to a subdirectory that falls below \\NAS\Disk1\JohnDoe, type:

cscript filesupload.wsf
  /srcPath:h:\photos /trgPath:
  "\\NAS\Disk 1\JohnDoe"

Notice that the value of the /trgPath parameter contains a space, so the example uses quotes to enclose the UNC path. If any parameter value you pass to the script contains spaces, be sure to enclose the value in quotation marks.

Once you pass all three parameters to FilesUpload.wsf, the most complex function in the script, CreateFolder Name, reads the file creation dates of all .jpg files in the source directory. The CreateFolderName function uses the dates to create a date-named subfolder to move the files to. So, assume that the .jpg files in the previous example were created between June 6, 2006 and June 15, 2006. The script creates a subfolder named 06-06-2006 to 06-15-2006, then moves the .jpg files to the subfolder. The code in Listing 3 shows how the CreateFolderName function begins constructing the target folder name and how it adds each file, along with the proper extension, to a filtered files collection. The code at callout A in Listing 3 verifies that the current file being enumerated has the extension that the /ext parameter specified when FilesUpload.wsf was called. The script stores the value of the /ext parameter in the extension variable. To determine whether the extension matches, the code at callout A calls another function, Extension-Length, to extract the extension length of the file being enumerated. FilesUpload.wsf doesn't assume that every file you might want to upload always has a three-character extension.

The main body of Listing 3 sets the value of the firstDate and lastDate variables as the script enumerates the FileSystemObject object's Files collection. Finally, callout B in Listing 3 adds a file that has the proper extension to the filteredFilesCollection Dictionary object. FileUploads uses this Dictionary object later in the script to move any files in that collection. The remainder of the CreateFolderName function (which I don't show in Listing 3) appends 0 to the month and day values to provide consistency in subfolder sorting and appearance. Because a four-digit year value will be fine for the next 7994 years, I figured I could leave that value alone.

After creating the subfolder, the MoveFiles function moves each file in the collection of filtered files to the target folder. The FileSystemObject object's MoveFile method can move multiple files by using wildcards. For example, the following code moves all .jpg files from one location to another:

fso.MoveFile c:\photos\*.jpg,

However, the code doesn't report the successful move of each file. In contrast, the MoveFiles function, which Listing 4 shows, moves all files that have a specified extension and reports the success or failure of each file move. The filteredFilesCollection Dictionary object that's populated in the CreateFolderFunction provides this enhanced reporting capability to the MoveFiles function.

The full FilesUpload.wsf script is more extensive than the snippets I show here. For example, the script contains several error-handling routines and useful functions for padding date values with zeros. Because the script contains more than I can cover here, I've made a point of commenting the full download heavily to give you a better sense of how it works.