File Access
From Xojo Documentation
Contents
All file access use done using a class called FolderItem. A FolderItem is anything that can be stored on a drive such as volumes, folders, files, applications, and documents.
Using the FolderItem class, you can get a reference to any such items on your drives. To read from a file, you need a FolderItem for it. To write to a file, you need a FolderItem. When you ask users to select a file using one of the file selectors, you get a FolderItem referring to the file they selected.
Once you have a FolderItem, you can refer to its properties (such as Name or path) and perform actions on it such as deleting or copying it.
Xojo has two FolderItem classes: FolderItem and Xojo.IO.FolderItem. The FolderItem class works on Desktop, Web and Console projects. The Xojo.IO.FolderItem class is for iOS projects, although it does work on the others. There are differences between these classes. This topic uses the FolderItem class. Refer to UserGuide:iOS Files for information on using files in iOS projects.
Shortcuts and Aliases
Shortcuts (aliases on Mac) are files that actually represent a volume, app, folder, or file stored in another location and possibly under another name. The FolderItem class contains properties and methods that allow you to either resolve the shortcut and work with the actual object or work with the object directly. The FolderItem constructor, Child and ChildAt methods automatically resolves a shortcut when it encounters it. However, each also provides an option to work with the shortcut itself.
File Locations
When you create your own files, you should avoid creating them beside the app itself. Modern operating systems restrict permissions on apps in the Application (or Program Files) folders so you would get an error in most cases. In addition, when running your app from Xojo on Windows, the build folder is recreated each time you run so any files you place alongside the app will be deleted.
You should instead use more appropriate locations for files such as the user's Documents folder or system folders such as Application Support or AppData. You can get access to these locations using SpecialFolder.Documents or SpecialFolder.ApplicationData. Refer to the next section for more information.
Accessing a File from a Specific Location
If you know the full path to a file and you wish to access the file, you can do so by specifying the path to the file.
For example, suppose you have a document called “Schedule” stored in the same folder as your app. The relative path starts with the folder your app is in. The FolderItem.Constructor function can be used to quickly get a reference to a file as seen in the following code:
To get the folder where your app resides: The full path (sometimes called the native path) to a volume, folder, app, or document starts with the volume name followed by the path delimiter character (a backslash on Windows and a forward slash on Mac and Linux), the names of any folders in the path (each separated by the path delimiter) and ending with the name of the item.
To create a native path to a file or folder, you should use the FolderItem.DriveAt shared method to build a full path to the item, starting with the drive it is on. You then use the Child method of the FolderItem class to navigate to the item. DriveAt returns a FolderItem for one of your mounted volumes. You specify the volume by passing an integer, indicating the volume. Volume 0 is the volume that contains the operating system — the “boot” volume.
The Parent property returns the FolderItem for the next item up in the path for the current FolderItem. It returns Nil if you try to get the parent of a volume. The Child and ChildAt methods let you access any items one level below the current FolderItem.
You can build a full path starting from a volume with the Child method. For example, if you want to get a FolderItem for the file “Schedule” in the folder “Stuff” on the boot volume, the code would be:
The following code works with a relative path. It uses the Parent property to get the FolderItem for the folder that contains the folder in which the app is located. Passing the empty string to the FolderItem.Constructor gets the current folder, so the parent of that folder is one level up in the hierarchy.
Once you have a FolderItem, you can (depending on what type of item it is) copy it, delete it, rename it, read from it or write to it, etc. You will learn how to read and write to files using FolderItems later in this topic.
The FolderItem.Constructor method has an optional parameter that allows you to pass a native path, a shell path, or a URL path. It uses the PathModes enumeration from the FolderItem class.
You specify the type of path by passing one of the class constants as the second parameter in a call to FolderItem.Constructor. For example, the following uses a shell path on Linux. It returns a FolderItem for the “Documents” folder in the home folder for the user “Joe.”
f = FolderItem("/home/Joe/Documents", FolderItem.PathModes.Shell)
If f.Exists Then
TextField1.Value = f.NativePath
Else
MessageBox("The FolderItem does not exist.")
End If
A URL path must begin with “file:///” The following example uses the URL path to the user’s “Documents” folder on Windows:
f = FolderItem("file:///C:/Documents%20and%20Settings/" _
+ "Joe%20User/My%20Documents/", FolderItem.PathModes.URL)
If f.Exists Then
MessageBox(f.NativePath)
Else
MessageBox("The FolderItem does not exist.")
End If
The FolderItem class’s properties NativePath, URLPath, and ShellPath contain the types of paths.
Accessing System Folders
Operating systems have specific locations for various folders that contain information, such as the Documents folder for the user.
Use the SpecialFolder module to get FolderItems representing these special system folders. The benefit of using this module rather than attempting to recreate the path manually, is that SpecialFolder always works and is correct across platforms (in most cases) and languages.
You obtain the desired FolderItem using the syntax:
where result is the FolderItem you want to obtain and FolderName is the name of the SpecialFolder function that returns that FolderItem. For example, the following gets a FolderItem for the "Application Support" folder on macOS and the "Application Data" directory on Windows:
Refer to SpecialFolder in the Language Reference for the complete list of supported functions and the FolderItems that are available for macOS, Windows and Linux.
Note that not all functions return FolderItems on all platforms. If a FolderItem is not defined on all platforms, you should use an alternative function that returns a FolderItem on every platform. Check that the result is not Nil before using the FolderItem. For example, SpecialFolder.Documents returns the current user’s Documents folder on macOS and Windows but returns Nil on Linux. On Linux, you should call SpecialFolder.Home instead. For example:
#If Not TargetLinux
f = SpecialFolder.Documents
#Else
f = SpecialFolder.Home
#EndIf
If f <> Nil Then
If f.Exists Then
// use the FolderItem
End If
Else
MessageBox("FolderItem is Nil!")
End If
Verifying the FolderItem
When you try to get a FolderItem, either of two things can go wrong. First, the path may be invalid. An invalid path contains a volume reference and/or a folder name that doesn’t even exist. For example, if you use FolderItem.DriveAt and pass it 3 when the user has only one drive, the FolderItem.DriveAt function returns a Nil value in the FolderItem instance, f. If you try to use any of the FolderItem class’s properties or methods on a Nil FolderItem, a NilObjectException error will occur. If the exception is not handled in some way, the app will quit.
Second, the path may be valid, but the file you are trying to access may not exist. The following shell code checks for these two situations:
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
If f.Exists Then
MessageBox(f.NativePath)
Else
MessageBox("File does not exist!")
End If
Else
MessageBox("Invalid path!")
End If
If the path is valid, the code checks the Exists property of the FolderItem to be sure that the file already exists; if the file doesn’t exist or the path is invalid, a warning message is displayed.
You can also handle an invalid path using an Exception Block. They are discussed in the Exception Handling section in the Debugging chapter.
Creating New FolderItems
You can create a FolderItem for an existing item by passing it the pathname. When you create a FolderItem with the New command, you can pass the path to the new FolderItem as an optional parameter. For example:
specifies the name of the new FolderItem and it is located in the same folder as your application (if you’re running in the IDE) or the same folder as the built app.
If you pass a FolderItem instead of a path, New will create a copy of the passed FolderItem. In this example, the FolderItem “f2” refers to a copy of the original FolderItem, not a reference to it.
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
f2 = New FolderItem(f)
End If
Creating Files on Web Servers
If your web app creates files (or folders) on web servers, including Xojo Cloud, you have to ensure you set the appropriate permissions to that you can later write to (or delete) them.
To do so, use the Permissions property of the FolderItem class. By default, files created on the web server get the permissions of the parent folder. This setting gives a file read/write access for all users:
Refer to the FolderItem.Permissions property in the Language Reference for specifics on how you can set permissions.
Deleting FolderItems
Once you have a FolderItem that represents an item that can be deleted, you can call the Delete method. The following example deletes the file represented by the FolderItem:
If the FolderItem is locked, an error will occur. You can check to see if the FolderItem is locked by checking the FolderItem’s Locked property. Deleting a FolderItem does not move the FolderItem to the trash -- it is deleted permanently from the volume.
f = SpecialFolder.Documents.Child("Schedule")
If f <> Nil Then
If f.Exists Then
f.Remove
End If
End If
Finding the Default Folder
Passing an empty string (two quotes with no characters in between them) to the FolderItem.Constructor function returns a FolderItem representing the folder your app is in. You can then use the FolderItem’s Item method to access all the items in the folder your app is in. The Item method returns an array of FolderItems in the folder. The array is one-based. You get a FolderItem for an item by passing the Item method the index of the item.
For example, the following method gets a FolderItem for the folder and populates a ListBox with the paths to each item in the Folder. It uses an iterator to loop through the FolderItems and the Child method to get a FolderItem for each item.
f = FolderItem("")
For Each file As Folderitem In f
If file <> Nil Then
ListBox1.AddRow(file.NativePath)
End If
Next
The following code returns a FolderItem that represents a file called “My Template” in a folder called “Templates” that is located in the same folder as the app:
Iterating Through Folder Contents
You may find that you need to iterate through a folderitem (that is a folder) in order to process all the files in it. Here is an example that deletes all the files in a folder:
Dim itemsToRemove() As FolderItem
Dim n As Integer = TargetFolder.Count
If n > 0 Then
For i As Integer = 1 To n
For Each file As FolderItem In TargetFolder
If file.Exists And Not file.Folder Then
itemsToRemove.AddRow(file)
End If
Next
End If
For i As Integer = 0 To itemsToRemove.LastIndex
itemsToRemove(i).Remove
Next
This code saves the files that are to be deleted in an array so that they can be deleted after all the files have been processed in order to improve performance. A more complete example is available in the Examples section of the FolderItem.Remove page in the Language Reference.
Prompting the User for Files and Folders
There are several methods available to allow the user to select files while using your apps. You may want to allow your user to specify a file to open, to specify the name of a file to save or you may want to let them choose a folder.
These commands can only be used in desktop apps.
Opening Files
The simplest method for prompting the user to select a file to open is to use the FolderItem.ShowOpenFileDialog function as follows:
The FolderItem.ShowOpenFileDialog function displays the Open File selector and returns a FolderItem object that represents the file the user selected. One or more file types (that have been defined in the File Type Group Editor or with the FileType class via the language.) must be passed to the FolderItem.ShowOpenFileDialog function. It presents only those file types to the user in its browser. In this way, the user can only open files of the appropriate type. To pass more than one file type, separate them with semicolons.
If the user clicks the Cancel button rather than the Open button in the Open File selector, FolderItem.ShowOpenFileDialog returns Nil. You will need to make sure the value returned is not Nil before using it. If you don’t, your app will crash with a NilObjectException. The following code shows how the code from the previous example should be written to check for a Nil object:
f = FolderItem.ShowOpenFileDialog(FileTypes1.jpeg)
If f <> Nil Then
MessageBox(f.ModificationDateTime.ToString)
End If
For more precise control, you can use the OpenFileDialog class to create an Open File selector. The class allows you to create a customizable open-file dialog box in which you can specify the following aspects of the dialog:
- Position (Left and Top properties)
- Default folder (InitialFolder property)
- Valid file types to show (Filter property)
- Text of Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties)
- Text that appears above the file browser (Title property)
- Text that appears below the file browser (PromptText property)
When you use the OpenFileDialog class, you create a new object based on this class and assign values to its properties to customize its appearance. The following example uses a custom prompt and displays only one file type:
dlg.InitialFolder = SpecialFolder.Documents
dlg.Title = "Select a MIF file"
dlg.Filter = FileTypes1.pdf
Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
// Proceed normally
Else
// User Cancelled
End If
Saving Files
The Save As selector is used to let the user choose a location in which to save a file and give the file to be saved a name.
The FolderItem.ShowSaveFileDialog function presents the Save As dialog box. The SaveAsDialog class allows you to create a customized version of this dialog. Both objects return a FolderItem that represents the file the user wishes to save. This is an important distinction because the file doesn’t exist yet. You must provide additional code that will create the file and write the data to the file. You will learn about creating files and writing data later in the UserGuide:Text Files and UserGuide:Binary Files topics.
When you call the FolderItem.ShowSaveFileDialog function, you define the type of file and the default name for the file (that appears in the Name field in the Save As selector). The file type (which is the first parameter of the function) is the name of any file type defined for the project in the File Types dialog box. Like the other functions that return FolderItems, you should make sure the FolderItem returned by FolderItem.ShowSaveFileDialog is not Nil before using it (which can happen if the user clicked Cancel).
This code displays a Save As selector with a default filename of “Untitled”:
f = FolderItem.ShowSaveFileDialog(FileTypes1.jpeg, "Untitled")
If f <> Nil And f.Exists Then
MessageBox(f.Name)
End If
When you use the SaveAsDialog class, you create a new object based on this class and customize the dialog by assigning values to its properties. You can customize the following aspects of the dialog:
- Position (Left and Top properties)
- Default directory (InitialFolder property)
- Valid file types to show (Filter property)
- Default filename (SuggestedFileName property)
- Text of the Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties)
- Text that appears above the file browser (Title property)
- Text that appears below the file browser (PromptText property)
The following code opens a customized save-file dialog box and displays the contents of the Documents directory in the browser area.
dlg.InitialFolder = SpecialFolder.Documents
dlg.Title = "Select a pdf file"
dlg.Filter = FileTypes1.Pdf
Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
// Proceed normally
Else
// User Cancelled
End If
Selecting Folders
Sometimes you need to have the user select a Folder rather than a file using the Folder selector. You can do this using the FolderItem.ShowSelectFolderDialog function:
If you need more control over this selector, you can use the SelectFolderDialog class instead. The class has properties to modify the:
- Action button caption
- Cancel button caption
- Initial Folder
- Position on screen
- Prompt text
- Title
- Default folder name
This code displays a customized Folder selector:
dlg.ActionButtonCaption = "Select"
dlg.Title = "Title Property"
dlg.PromptText = "Prompt Text"
dlg.InitialFolder = SpecialFolder.Documents
Var f As FolderItem
f = dlg.ShowModal
If f <> Nil Then
// Use the FolderItem here
Else
// User cancelled
End If
File Formats
When saving your own files, you have to decide on the file format. You can use a text-based format such as JSON or XML or choose a binary format where you can include whatever types of data you want.
If you need to save a combination of data such as both text and graphics, here are some other options:
- You could use a text file and include the path to the related graphics files. This doesn’t work great for transferring files around, though.
- Although a text file cannot contain graphics you could still use a text format using JSON or XML and then use the EncodeBase64 and DecodeBase64 functions to convert binary information such as pictures to text, inserting them into JSON/XML as appropriate, but this will make the files pretty large.
- You could save the file as a binary file and embed everything in the file at specific locations, but this is less common these days.
- You could use a SQLite database as the file format and create one or more tables to contain what you want to save. This is pretty useful and fully cross platform.
- On MacOS there is a concept called a “package” that is essentially a folder that is treated as a file. This is not cross platform but is a way to combine different types of data. More information from Apple: Document Packages
See Also
FolderItem, Xojo.IO.FolderItem, OpenFileDialog, SaveAsDialog, SelectFolderDialog classes; SpecialFolder module; FolderItem.Constructor(f_as_FolderItem), FolderItem.ShowOpenFileDialog, FolderItem.ShowSaveFileDialog, FolderItem.ShowSelectFolderDialog, FolderItem.DriveAt, FolderItem.DriveCount functions; UserGuide:Framework topic