This sample project shows a technique to programmatically reduce the storage size of a full file system and to archive older files to a different location. In the last few years, the number of files used by a company has exponentially grown. This has been caused by many factors, first of all the diffusion of email as a vehicle of any sort of document. The documents are written, corrected, reviewed, published, and sometimes inserted in a knowledge base and often they get forgotten. During this process, many copies are stored on a file system and then backed up. A lot of these documents, after being published, get "dead": they are archived, and accessed only for reading. Archive systems are based on an access ranking. While the document is still reviewed, it must be quickly accessible and updateable and backed up daily. Once the file gets archived, it could be marked as read-only and it could not be backed up often.
Using the code
Firstly, it is necessary to identify which files and which directory contains the archived documents and then it is necessary to define after how much time the documents can be moved in to the archive storage. After this process, the document is moved to a different location (store or server) and it is substituted with a shortcut. The form has two radio buttons where the user can choose if the archiving process works on files older than a specific date rather than a number of months. The user must also select the root directory, where the process starts, and the destination archive directory. You must pay attention because the destination directory is used as the "destination path" in the shortcuts, hence it must be a location accessible to all users that could see the shortcuts. The better choice is to use an UNC path which points to a share. By checking "Analysis only", you can only preview the results. This feature inserts into a JET database all files which would be archived, so the user can preview how much storage will be freed by the process.
In the project directory, there are two sample directories: "SOURCE FOLDER" and "ARCHIVE FOLDER". The source folder contains the sample files. Make sure that the archive folder is shared and is accessible for reading and writing from "\\127.0.01\archive folder". Run the project and select today as the specific date. After executing the archive process, you can view the text file that has been moved to the archive folder, and in the source directory, it has been replaced by a shortcut pointing to "\\127.0.0.1\archivefolder\text document.txt".
Points of interest
The main process is performed by the function
ArchiveFile which take an input parameter of type
FileInfo. This function checks if the input file is older enough to be archived, if so, builds the new path, moves the file in to the new location, and creates the shortcut.
Private Sub ArchiveFile(ByVal fi As FileInfo)
Dim d As Date
If Me.RadioButton1.Checked Then
d = Me.DateTimePicker1.Value
d = Date.Now.AddMonths(-CInt(Me.TextBox3.Text))
If fi.LastWriteTimeUtc < d Then
Dim sf As String = fi.FullName
Dim df As String = Me.dest_path.FullName
sf = sf.Remove(0, Me.start_path.FullName.Length)
df += sf
Dim nfi As FileInfo
nfi = New FileInfo(df)
Dim ndi As DirectoryInfo = New DirectoryInfo(nfi.DirectoryName)
If Not ndi.Exists Then
Dim oldfi As FileInfo
oldfi = New FileInfo(fi.FullName)
Debug.WriteLine("Archived: " & nfi.FullName)
fi.Attributes = fi.Attributes And Not FileAttributes.ReadOnly
Catch ioex As System.IO.IOException
Private Sub CreateShortCut(ByVal fi As FileInfo, ByVal tfi As FileInfo)
m_Shortcut = New ShellShortcut(fi.DirectoryName & "\" & fi.Name & ".lnk")
.Path = tfi.FullName
.WorkingDirectory = tfi.DirectoryName
.Description = ""
.WindowStyle = ProcessWindowStyle.Normal
fi = New FileInfo(fi.DirectoryName & "\" & fi.Name & ".lnk")
fi.Attributes += FileAttributes.ReadOnly
Another interesting point is the class
ShellShortcut that allows the creation of system shortcuts which use a wrapper class to the WinAPI.
Public Class ShellShortcut
Private Const INFOTIPSIZE As Integer = 1024
Private Const MAX_PATH As Integer = 260
Private Const SW_SHOWNORMAL As Integer = 1
Private Const SW_SHOWMINIMIZED As Integer = 2
Private Const SW_SHOWMAXIMIZED As Integer = 3
Private Const SW_SHOWMINNOACTIVE As Integer = 7
#If [UNICODE] Then
Private m_Link As IShellLinkW
Private m_Link As IShellLinkA
Private m_sPath As String
Public Sub New(ByVal linkPath As String) […]
Public Sub Dispose() Implements IDisposable.Dispose
Public Property Arguments() As String […]
Public Property Description() As String […]
Public Property WorkingDirectory() As String […]
Public Property Path() As String […]
Public Property IconPath() As String […]
Public Property IconIndex() As Integer […]
Public ReadOnly Property Icon() As Icon […]
Public Property WindowStyle() As ProcessWindowStyle […]
Public Property Hotkey() As Keys […]
Public Sub Save()[…]
Public ReadOnly Property ShellLink() As Object […]
This technique works if nobody moves the archived folder or if nobody changes the file system structure or, again, if nobody deletes the files or the shortcuts. To prevent these issues, I've set (manually) ACEs on files and directories with these restrictions.
I think that this tool can be modified to become windowless and take the parameters from an XML configuration file. It can manage several tasks, each one for a specific directory. So, it can manage: directory1, only files not modified since 6 months ago. directory2, only files not modified since 12 months ago. And so on... This way, it can be scheduled weekly or monthly.