13 February 2013
Wednesday FAQs: Sub-Folder and File Access
It’s Wednesday and time for another set of frequently asked questions (FAQs). Here are some FAQs about accessing sub-folders and files.
1. How do you create a new sub-folder within the Documents or Temporary directory?
Sub-folders can be added to directories using the Lua File System (LFS). The Resource directory is read-only and cannot be modified.
Here is how you can create an Images folder in the Documents directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
local lfs = require "lfs" -- get raw path to app's Documents directory local docs_path = system.pathForFile( "", system.DocumentsDirectory ) -- change current working directory local success = lfs.chdir( docs_path ) -- returns true on success local new_folder_path local dname = "Images" if success then lfs.mkdir( dname ) new_folder_path = lfs.currentdir() .. "/" .. dname end |
You can find more information about LFS here.
2. How do you access (read or write) a file that has been placed in a sub-folder?
You can access files in a sub-folder in two ways depending on what you want to do with the file. If you want to display an image or play a sound from the file, you concatenate the sub-folder name with the file name and then supply the base directory. For example, if you want to display the cat.png file in the Images sub-folder in the Documents directory, do the following:
1 |
local catImage = display.newImage( "Images/cat.png", system.DocumentsDirectory, 0, 0 ) |
Note that you don’t use system.pathForFile in API calls that require a baseDirectory parameter (e.g., display.newImage, display.newImageRect, audio.loadSound, etc.).
If you want to open the readme.txt file in that same directory, do the following using system.pathForFile:
1 2 3 |
local path = system.pathForFile( "Images/readme.txt", system.DocumentsDirectory ) local fileHandle = io.open( path ) -- You can now use fileHandle:read or fileHandle:write to read or write the file. |
The fileHandle will not be nil if the file exists, which leads into our next question.
3. How do you test that a file exists in a folder or sub-folder?
The following function can be called from your code to see if a file exists in a folder or sub-folder. Just remember to append the sub-folder name to the file name before calling this function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
---------------------------------------------------------------------------------- -- doesFileExist -- -- Checks to see if a file exists in the path. -- -- Enter: name = file name -- path = path to file (directory) -- defaults to ResourceDirectory if "path" is missing. -- -- Returns: true = file exists, false = file not found ---------------------------------------------------------------------------------- -- function doesFileExist( fname, path ) local results = false local filePath = system.pathForFile( fname, path ) -- filePath will be nil if file doesn't exist and the path is ResourceDirectory -- if filePath then filePath = io.open( filePath, "r" ) end if filePath then print( "File found -> " .. fname ) -- Clean up our file handles filePath:close() results = true else print( "File does not exist -> " .. fname ) end print() return results end |
Here’s how the above function is called.
1 2 3 4 5 |
-- Checking for file in Documents directory local results = doesFileExist( "Images/cat.png", system.DocumentsDirectory ) -- or checking in Resource directory local results = doesFileExist( "Images/cat.png" ) |
4. How do you copy a file to a sub-folder?
The following function allows copying a file from one folder to another. One common use is to copy a file shipped in the Resource directory to the Documents directory. You must create any sub-folders (if they don’t exist) before using this function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
---------------------------------------------------------------------------------- -- copyFile( src_name, src_path, dst_name, dst_path, overwrite ) -- -- Copies the source name/path to destination name/path -- -- Enter: src_name = source file name -- src_path = source path to file (directory), nil for ResourceDirectory -- dst_name = destination file name -- overwrite = true to overwrite file, false to not overwrite -- -- Returns: false = error creating/copying file -- nil = source file not found -- 1 = file already exists (not copied) -- 2 = file copied successfully ---------------------------------------------------------------------------------- -- function copyFile( srcName, srcPath, dstName, dstPath, overwrite ) local results = false local srcPath = doesFileExist( srcName, srcPath ) if srcPath == false then -- Source file doesn't exist return nil end -- Check to see if destination file already exists if not overwrite then if fileLib.doesFileExist( dstName, dstPath ) then -- Don't overwrite the file return 1 end end -- Copy the source file to the destination file -- local rfilePath = system.pathForFile( srcName, srcPath ) local wfilePath = system.pathForFile( dstName, dstPath ) local rfh = io.open( rfilePath, "rb" ) local wfh = io.open( wfilePath, "wb" ) if not wfh then print( "writeFileName open error!" ) return false -- error else -- Read the file from the Resource directory and write it to the destination directory local data = rfh:read( "*a" ) if not data then print( "read error!" ) return false -- error else if not wfh:write( data ) then print( "write error!" ) return false -- error end end end results = 2 -- file copied -- Clean up our file handles rfh:close() wfh:close() return results end |
Here is how to use the copy file routine to copy the readme.txt file from the Resource to the Documents directory.
1 2 |
copyFile( "readme.txt", nil, "readme.txt", system.DocumentsDirectory, true ) local catImage = display.newImage( "cat.png", system.DocumentsDirectory, 0, 0 ) |
5. What are the Android restrictions concerning files?
File access in Corona is based on the underlining Operating System, which will vary depending on the platform. On iOS devices, you can access files in the Resource directory (where main.lua is located) and the Documents and Temporary directories. On Android, accessing the Resource directory is limited because it’s not a real directory, but files are enclosed in a zip file. Corona allows directly loading images and audio files using audio and image APIs, but has limited access to Resource files using the file I/O APIs.
Because of this limitation on Android, if you have files shipped in the Resource directory that you want to copy to another directory (e.g., Documents), you will need to change the file name in the resource directory so it can be access by the file I/O APIs. For example, if you want to move an image file from the Resource to the documents directory, it must be renamed with a different extension so it can be accessed. Our cat.png file would need to be called cat.png.txt so it could be copied.
Here is how you would copy the cat.png file to the Documents directory on Android (assuming it was stored as cat.png.txt).
1 2 |
copyFile( "cat.png.txt", nil, "cat.png", system.DocumentsDirectory, true ) local catImage = display.newImage( "cat.png", system.DocumentsDirectory, 0, 100 ) |
The file extensions that cannot be read in the Resource directory by Android are: html, htm, 3gp, m4v, mp4, png, jpg, and rtf.
The above technique works for all platforms so if you make it work for Android, it will work everywhere.
That’s it for today’s FAQs. I hope you enjoyed them and even learned a few things.
Jose
Posted at 09:27h, 14 FebruaryGood post Tom! Got here from my question on the forum about the pathForFile warnings (http://developer.coronalabs.com/forum/2013/02/14/pathforfile-warnings), but I’m afraid I have the same problem with your code.
Is there any other way to get the existence of a file skipping the warnings pathForFile causes when it doesn’t find the file???
Anyway, the points 1 & 4 are of great help for my next projects, thanks!
Alberto
Posted at 09:38h, 15 FebruaryHi!
Concerning Android devices, is it possible to create sub-folders and copy files to external storage?
My Android device has got 2Gb of internal storage and 16Gb of nand flash storage.
The app I’m developing let user downloads catalogs about 1Gb size, so I need to use the 16Gb from flash storage.
Is it possible?
Thanks in advance!
Alberto.
Alberto
Posted at 01:14h, 19 FebruaryJust another issue:
I’ve created a sub directory in system.DocumentsDirectory and I want to download files directly into that sub-folder I created. When i try and feed it a path other than system base directories, it ignors them and reverts to the default of system.DocumentsDirectory, in spite of adding the subdirectory name.
Is not possible to directly download to the sub-foldr I’ve just created?
Alberto.
lessmsios
Posted at 17:36h, 01 AprilIn the copy function the line:
local srcPath = doesFileExist( srcName, srcPath )
will over write the parameter srcPath. If you use this function as is without renaming that test it will throw an error.