Posted on by

FAQ IconIt’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.

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:

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:

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.

----------------------------------------------------------------------------------
-- 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.

-- 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.

----------------------------------------------------------------------------------
-- 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.

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).

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.


Posted by . Thanks for reading...

4 Responses to “Wednesday FAQs: Sub-Folder and File Access”

  1. Alberto

    Hi!

    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.

    Reply
  2. Alberto

    Just 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.

    Reply
  3. lessmsios

    In 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.

    Reply

Leave a Reply

  • (Will Not Be Published)