Just decided to share my solution to the problem I had, that per default display.save() can write image files only to the system directories, because of app sandboxing. My task was to create a button (newRect) with text (newText) on it, and then save it to my desktop (or any arbitrary directory) as a PNG file, created from the button display group. I almost gave up, thinking it impossible, but in the end managed to do it (yay!).
(I am using macOS, but the technique should work on Windows as well):
used resources:
1.tinyfiledialogs plugin - to get user-friendly directory path selector
2. this guide - the part about “Copying Files to Subfolders”, with some modifications to the code
In a nutshell - use display.save() to save your display object (in my case the display group containing the rectangle and the text object on it) to the temporary directory ( system.TemporaryDirectory
), then use tinyfiledialogs to get the string of the path you actually need, and the use the code from the guide (with the modifications below) to copy the image from the temp directory to the one you actually want.
The modified code, for copying files from the sandbox to wherever you want:
Check if the file in the system directories (sandbox) exists:
local function doesFileExistSB( fname, path )
local results = false
-- Path for the file
local filePath = system.pathForFile( fname, path )
print("file path is "..filePath)
if ( filePath ) then
local file, errorString = io.open( filePath, "r" )
if not file then
-- Error occurred; output the cause
print( "File error: " .. errorString )
else
-- File exists!
print( "File found: " .. fname )
results = true
-- Close the file handle
file:close()
end
end
return results
end
Check if the file exists outside the sandbox:
local function doesFileExist( fname, path )
local results = false
-- Path for the file
local filePath = path..fname
print("file path is "..filePath)
if ( filePath ) then
local file, errorString = io.open( filePath, "r" )
if not file then
-- Error occurred; output the cause
print( "File error: " .. errorString )
else
-- File exists!
print( "File found: " .. fname )
results = true
-- Close the file handle
file:close()
end
end
return results
end
copy from the sandbox to the directory you want:
M.copyFile = function( srcName, srcPath, dstName, dstPath, overwrite )
local results = false
local fileExists = doesFileExistSB( srcName, srcPath )
if ( fileExists == false ) then
return nil -- nil = Source file not found
end
-- Check to see if destination file already exists
if not ( overwrite ) then
if ( doesFileExist( dstName, dstPath ) ) then
return 1 -- 1 = File already exists (don't overwrite)
end
end
-- Copy the source file to the destination file
local rFilePath = system.pathForFile( srcName, srcPath )
local wFilePath = dstPath..dstName
local rfh = io.open( rFilePath, "rb" )
local wfh, errorString = io.open( wFilePath, "wb" )
if not ( wfh ) then
-- Error occurred; output the cause
print( "File error: " .. errorString )
return false
else
-- Read the file and write to the destination directory
local data = rfh:read( "*a" )
if not ( data ) then
print( "Read error!" )
return false
else
if not ( wfh:write( data ) ) then
print( "Write error!" )
return false
end
end
end
results = 2 -- 2 = File copied successfully!
-- Close file handles
rfh:close()
wfh:close()
return results
end
A thing to be careful about:
If you build your app for the mac app store, and submit it to Apple, you probably need to set its entitlements in the build.settings file, so it gets access to directories outside its sandbox, probably like this:
osx = {
entitlements = {
["com.apple.security.assets.pictures.read-write"] = true, -- get access to user's Pictures folder
["com.apple.security.files.user-selected.read-write"] = true, -- get arbitrary access?
},
},
More info about entitlements here.
And that’s it. I hope this can be useful to someone. Cheers!