Filter files into silo folders

Recently a number of my friends have hard hard drive failures and have asked me to retrieve data from their hard drives.  I’ve been reasonably successful in each case recovering well over 95% of the data in each case - thanks not to me but the proliferation of free data recovery tools out there on the Internet.

The last one I’ve been working on however has had a particularly mangled folder structure such that a large number of the recovered files haven’t been able to be assigned the correct file name and folder.  The recovery software has auto generated file names and placed them in a single folder … all 245,528 of them.  As you might expect Windows isn’t all that happy with that many files in a single folder when you want to interact with them.

Windows Explorer is incredibly slow to interact with this amount of content and even using SnowBird the speed of access was prohibitive.  I had a need to somehow rearrange the files into some other structure and since the naming is random, the folder structure does not need to be particularly meaningful - just practical to sort through.  Given there was no (free) software up to the job and being the consummate IT support chap I am  I decided to script my way around the problem.

I wrote a VBScript to process the folder of files and to “silo” the files off into groups within other folders.  A couple of constants at the top of the script tell it which folder to process and how many files to put into each folder.  The script then calculates how many folders to create and starts to process them.  I’ve used my generic progress window to show what file is being moved and how far through the process the script is.

Option Explicit

Const FOLDER = "c:\Lots of files\"

Dim strFolderToProcess
Dim objFSO, objFolder, objFile
Dim colFiles
Dim intFiles, intNumberofSilos, intFilesPerSilo, intTotalNoFiles
Dim objWindow

strFolderToProcess = FOLDER
intFilesPerSilo = FILES_PER_SILO

Set objWindow = new ProgressWindow
objWindow.Initialise "Silo Files", 6, 750, 250

'Initialise the file system objects
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder= objFSO.GetFolder(strFolderToProcess)
Set colFiles = objFolder.Files

objWindow.UpdateRegion 1, "Calculating number of silos to create..."

intTotalNoFiles = colFiles.Count
intNumberofSilos = Ceil(intTotalNoFiles/intFilesPerSilo)

'Create Silo folders
objWindow.UpdateRegion 1, "Creating silos..."
CreateSiloFolders intNumberOfSilos

'Move files into silo folders
objWindow.UpdateRegion 1, "Calculating number of silos to create..."
MoveFilesToSilos intNumberOfSilos

objWindow.UpdateRegion 1, "Processing complete!"
WScript.sleep 2000

'# SUBS #

Sub CreateSiloFolders(p_intTotalNumberOfSiloFolders)
	Dim intCounter
	Dim strSiloPath

	For intCounter = 0 to (p_intTotalNumberOfSiloFolders - 1)
		strSiloPath = strFolderToProcess & _
			ExpandNumber(intCounter, p_intTotalNumberOfSiloFolders - 1)
		objWindow.UpdateRegion 2, "Creating silos " & strSiloPath
		CreateFolder strSiloPath
End Sub

Sub MoveFilesToSilos(p_intTotalNumberOfSiloFolders)
	Dim intCounter, intFolder, intFilesProcessed

	intCounter = 1
	intFolder = 0
	intFilesProcessed = 0

	objWindow.UpdateRegion 3, "<b>Total Progress</b>"
	objWindow.UpdateRegion 5, "<b>Folder Progress</b>"

	For each objFile in colFiles
		objWindow.UpdateRegion 2, "Moving " & objFile.Name & " to " & _
			strFolderToProcess & ExpandNumber(intFolder, p_intTotalNumberOfSiloFolders - 1) & _
				"\" & objFile.Name

		objFile.Move(strFolderToProcess & ExpandNumber(intFolder, p_intTotalNumberOfSiloFolders - 1) _
			& "\" & objFile.Name)

		If intCounter < intFilesPerSilo then
			intCounter = intCounter + 1
			intFolder = intFolder + 1
			intCounter = 1
		End If

		objWindow.UpdateRegionProgress 4, (intFilesProcessed/intTotalNoFiles)*100, 65
		objWindow.UpdateRegionProgress 6, (intCounter/FILES_PER_SILO)*100, 30

		intFilesProcessed = intFilesProcessed + 1

	objWindow.UpdateRegion 3, "<b>TRANSFER COMPLETE!</b>"
	objWindow.ClearRegion 4
	objWindow.ClearRegion 5
	objWindow.ClearRegion 6
End Sub

'# FUNCS #

Function ExpandNumber(p_strNum, p_strBase)
	ExpandNumber = p_strNum
	While len(ExpandNumber)<len(p_strBase)
		ExpandNumber = "0" & ExpandNumber
End Function

Function CreateFolder(p_strFolderPath)
	If objFSO.FolderExists(p_strFolderPath) Then
		CreateFolder = false
	   	Set objFolder = objFSO.CreateFolder(p_strFolderPath)
		CreateFolder = true
	End If
End Function

Function Ceil(p_Number)
	Ceil = 0 - INT( 0 - p_Number)
End Function

'#### CLASSES ###
'Create an instance of this class to create a progress window based on an HTML page
'displayed in an Internet Explorer window.
Class ProgressWindow

	'Progress Window Properties
	Dim intWidth
	Dim intHeight
	Dim intRegions
	Dim strTitle
	Dim objProgress

	'Create the progress window
	'The first parameter is the title of the progress window.
	'The second parameter is the number of regions on the page you wish to 
	'	be able to update independently.
	'The third parameter is the width of the window in pixels.
	'The fourth parameter is the height of the window in pixels.
	Sub Initialise(p_strTitle, p_intRegions, p_intWidth, p_intHeight)
		'Set class properties
		strTitle = p_strTitle
		intRegions = p_intRegions
		intWidth = p_intWidth
		intHeight = p_intHeight

		'Create the progress window
		CreateMSIEWindow objProgress
	End Sub

	'Display the basic window
	Sub CreateMSIEWindow(p_objMSIE)
		Dim strHTML
		Dim intCounter

		strHTML = ""
		For intCounter = 1 to intRegions
			strHTML = strHTML & _
				"<div id='REGION" & intCounter & "' align='left'></div>"
		strHTML = "<body>" & strHTML & "</body>"

		Set p_objMSIE = CreateObject("InternetExplorer.Application")
		With p_objMSIE
			.Navigate2 "about:blank"
			Do While .readyState <> 4
				Wscript.sleep 10
			.Document.Title = strTitle
			.Document.Body.InnerHTML = strHTML
			.Document.Body.Scroll = "no"
			.Toolbar = False
			.StatusBar = False
			.Resizable = False
			.Width = intWidth
			.Height = intHeight
			.Left = 0
			.Top = 0
			.Visible = True
		End With
	End Sub

	'This should be called to close the window
	Sub Destroy()
	End Sub

	'This method can be used to update the content of a specified region
	Sub UpdateRegion(p_intRegion, p_strContent)
		objProgress.Document.All("REGION" & Cstr(p_IntRegion)).innerHTML = p_strContent
	End Sub

	'This method clears a specified region.
	Sub ClearRegion(p_intRegion)
		UpdateRegion p_intRegion, ""
	End Sub

	'This method clears all of the regions at once.
	Sub ClearAllRegions()
		Dim intRegion

		For intRegion = 1 to intRegions
			UpdateRegion intRegion, ""
	End Sub

	'Set a progress bar as a region...
	'The first parameter is which region.
	'The second parameter is the percentage completion of the bar.
	'The third parameter is the number of characters that make up the bar.
	Sub UpdateRegionProgress(p_intRegion, p_intPrecentageProgress, p_intCharacterRange)
		'These constants are used to create the progress bar
		Const SOLID_BLOCK_CHARACTER = "&#x2588;"
		Const EMPTY_BLOCK_CHARACTER = "&#x2588;"
		Const SOLID_BLOCK_COLOUR = "#ffcc33;"
		Const EMPTY_BLOCK_COLOUR = "#666666;"

		Dim intSolidBlocks, intEmptyBlocks, intCounter
		Dim strProgress

		'Calculate how many blocks we need to create
		intSolidBlocks = round(p_intPrecentageProgress / 100 * p_intCharacterRange)
		intEmptyBlocks = p_intCharacterRange - intSolidBlocks

		'Build the progress so far blocks
		strProgress = "<span style='color: " & SOLID_BLOCK_COLOUR & "'>"
		intCounter = 0
		While intCounter < intSolidBlocks
			strProgress = strProgress & SOLID_BLOCK_CHARACTER
			intCounter = intCounter + 1

		'Build the progress to be met blocks
		strProgress = strProgress & "</span><span style='color: " & _
		intCounter = 0
		While intCounter < intEmptyBlocks
			strProgress = strProgress & EMPTY_BLOCK_CHARACTER
			intCounter = intCounter + 1
		strProgress = strProgress & "</span>"

		'Set the specified region to be the blocks
		UpdateRegion p_intRegion, strProgress
	End Sub

End Class
Author: Stephen Millard
Tags: | utilities | vbs |

Buy me a coffeeBuy me a coffee

Related posts that you may also like to read