Android Question Recursively copy folders and files from ExternalStorage to DirInternal and viceversa.

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

from here I search to adapt:
https://www.b4x.com/android/forum/threads/b4x-copyfolder-deletefolder.69820/#content

I have this sub to recursively copy folders and files from ExternalStorage to DirInternal, but I cannot get it to work for subfolders. Folders and files are copied on first sub-folder but successive sub-folders are created to the root. Please, can someone help me to know how to fix it ?

B4X:
Sub Class_Globals
   Private lastPath As String
End Sub

Private Sub CopyFolderExternalToInternal (Source As ExternalFile, targetFolder As String) As Boolean

    If Source.IsFolder = False Then Return False

    If File.Exists(targetFolder,  "") = False Then File.MakeDir(targetFolder, "")
 
    For Each f As ExternalFile In Storage.ListFiles(Source)
        Log("File name: " & f.Name)
        If f.IsFolder Then
            lastPath = f.Name
            Log("Last folder: " & f.Name)
            If File.Exists(File.DirInternal,  lastPath) = False Then File.MakeDir(File.DirInternal,  lastPath)
            CopyFolderExternalToInternal(f, targetFolder)
            Continue
        End If
 
        ' External to Internal
        Dim iStream As InputStream, oStream As OutputStream
        Dim ef As ExternalFile = Storage.FindFile(Source, f.Name)
        If ef.IsInitialized = True And ef.Length > 0 Then
            Log("Save file: " & ef.Name & " to " & File.Combine(File.Combine(File.DirInternal,lastPath), ef.Name))
            iStream = Storage.OpenInputstream(ef)
            oStream = File.OpenOutput(File.Combine(File.DirInternal,lastPath), ef.Name, False)
            File.copy2(iStream, oStream)
            iStream.Close
            oStream.Close
        Else
            Log("CopyFolderExternalToInternal: Cannot open file from external storage")
            Return False
        End If
    Next
 
    Return True
End Sub

I have the opposed sub that do the inverse, so copy recursively folders and files from DirInternal to ExternalStorage.
Currently I do not tested it. I need both subs in my application. Please, do you see something wrong here ?
B4X:
Private Sub CopyFolderInternalToExternal(Source As String, targetFolder As ExternalFile) As Boolean
 
    If File.IsDirectory(Source, "") = False Then Return False
 
    '  Storage.FindDirOrCreate(targetFolder, "")
 
    For Each fName As String In File.ListFiles(Source)
        If File.IsDirectory(Source, fName) Then
            CopyFolderInternalToExternal(File.Combine(Source, fName), Storage.FindDirOrCreate(targetFolder, fName))
            Continue
        End If
 
        ' Internal to External
        Dim iStream As InputStream, oStream As OutputStream
        Dim ef As ExternalFile = Storage.FindFileOrCreate(targetFolder, fName)
        If ef.IsInitialized = True Then
            iStream = File.OpenInput(File.DirAssets, fName)
            oStream = Storage.OpenOutputStream(ef)
            File.copy2(iStream, oStream)
            iStream.Close
            oStream.Close
        Else
            Log("CopyFolderInternalToExternal: Cannot open file from external storage")
            Return False
        End If
 
    Next
 
    Return True
End Sub

Thanks in advance for any tip.
 
Last edited:

max123

Well-Known Member
Licensed User
Longtime User
I will try it @Erel.
Thanks
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I tried to apply your code @Erel

and this is a working sub that recursively copy an ExternalStorage folder, subfolders and files mantaining
the structure inside a DirInternal. Here seem to work, please do you see something wrong ?

Now I have to manage to do the same but from DirInternal to ExternalStorage.

Thanks

B4X:
Private Sub CopyExternalToInternal(Source As ExternalFile, targetFolder As String) As Boolean

    If Source.IsInitialized = False Or Source.IsFolder = False Then Return False

'    Log("COPY [" & Source.Name & "] CONTENT --> " & targetFolder)
      
    For Each f As ExternalFile In Storage.ListFiles(Source)
'        Log("File name: " & f.Name)
      
        If f.IsFolder Then
'            Log("TEST IF FOLDER EXISTS: " & File.Combine(targetFolder, f.Name))         
            If File.Exists(targetFolder, f.Name) = False Then File.MakeDir(targetFolder, f.Name)  ' THANKS EREL         
            CopyExternalToInternal (f, File.Combine(targetFolder, f.Name))  ' THANKS EREL
            Continue
        End If
      
        ' External to Internal
        Dim iStream As InputStream, oStream As OutputStream

        If f.IsInitialized = True And f.Length > 0 Then
            Log("IMPORT FILE: [" & f.Name & "]   -->  " & File.Combine(targetFolder, f.Name))
          
            'If LogIsStarted Then
                'LVLog.AddSingleLine("IMPORT FILE:  " & f.Name)
                'LVLog.SetSelection(LVLog.Size-1)
            'End If
              
            iStream = Storage.OpenInputstream(f)
            oStream = File.OpenOutput(targetFolder, f.Name, False)         
            File.copy2(iStream, oStream)
            iStream.Close
            oStream.Close
        Else
            Log("Cannot open file from external storage")
            Return False
        End If
    Next
  
    Return True
End Sub

' USAGE:

Dim ExternalAppPath As ExternalFile = Storage.Root
Dim InternalAppPath As String = File.Combine(File.DirInternal, Storage.Root.Name)

Dim Success As Boolean = CopyExternalToInternal(ExternalAppPath, InternalAppPath)
If Success Then
    Log("SUCCESSFULLY IMPORTED FOLDER [" & ExternalAppPath.Name & "]")
    ToastMessageShow("IMPORT DONE", False)
Else
    Log("CANNOT IMPORT FOLDER [" & ExternalAppPath.Name & "]")
    ToastMessageShow("IMPORT ERROR", False)
End If
 
Last edited:
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
If you are trying to copy a folder tree and file structure from internal to external storage then you are going to run into trouble with Google's restrictions for file system access. You cannot create folders or save files in external storage without involvement from the User which obviously will not fit into a smooth recursive process.
 
  • Like
Reactions: zed
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
If you are trying to copy a folder tree and file structure from internal to external storage then you are going to run into trouble with Google's restrictions for file system access. You cannot create folders or save files in external storage without involvement from the User which obviously will not fit into a smooth recursive process.
Hi @Brian Dean,

thanks for tip.

My app acquire ExternalStorage permissions.

On first boot show a file manager where the user create the app folder and grant permissions.
After this the app create some sub folders and copy some files from asset dir.

Because ExternalStorage api is very slow, I won't work on this directly,
I need fast access, so I copy the same structure and files to DirInternal, this even
permits to import some files the user can put with a file manager inside external
storage folders.

Note that for external storage I mean the phone internal memory, not usb sticks, but should not bad if I can add usb sticks read too.

I do not need to change the folder structure, I just have 3 folders:
- Files
- Images
- Library

My app have 2 menu voices
- Import files
- Export files

The first copy all files from external storage folders (mantaining folder structure) to DirInternal
The second do the opposite, copy all files from DirInternal (mantaining folder structure) to external storage.

The app should be released on Google Play Store, yes, do you think Google do not accept this ?

If Google does not accept, this is a big problem, I need to have some folders where user can put and get files to be used by my app and created from my app.

Personally if this cannot be done, I do not know at all the external storage use what it's for.

Thanks
 
Last edited:
Upvote 0

Brian Dean

Well-Known Member
Licensed User
Longtime User
Note that for external storage I mean the phone internal memory, not usb sticks

"External memory" means any memory that is not private to the app; it does not mean "removable memory". So this definitely affects your plan.

The app should be released on Google Play Store, yes, do you think Google do not accept this ?

I cannot say. Google would probably accept a plan from a major company, like a 'phone manufacturer, but reject the same plan from an individual, like yourself, but I do not know. Here is a link to their policy on the subject.

Personally if this cannot be done, I do not know at all the external storage use what it's for.

A lot of external files are images, messages and similar personal data that Google probably feel are likely to be wrongly used by bad actors and therefore access should only be given to known trustworthy entities. That does seem sensible in this day and age. Remember that access is still available with specific User consent, but that will probably not work in a recursive procedure.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I need @Erel confirmation for this ...

My app is pretty finished,, just I need to adapt it to Google policies before I will try to release a free version.

I can use this sub I posted and other that do the opposite, and release the app on Google Play Store ?

Thanks
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I'm not sure that I understand the question. If you are using ExternalStorage then you are first requesting the user to grant access to a specific folder and its subfolder.
Yes Erel, thanks for reply,

I do it... I grant access to a specific external storage folder, that is the app folder.
After I grant access I create here some sub-folders and copy some files inside from DirAssets (just for demos).

After this I only copy recursively folders and files to DirInternal and viceversa, so Import or Export.

I ask you just because @Brian Dean reply confused me a bit, and I want ensure this is something accepted by Google policy.

Only ExternalStorare (Storage.Root) with grant permissions and DirInternal are involved, no DirRootExternal, so I think this shoud not a problem with Google, I'm wrong ?
I think that when access is grant on a folder, then I can do file operations on this folder and sub-folders by code without request other permissions to the user for any file, right?

Here my code extraction, that better explain what I do.... better than 100000 words:
B4X:
Sub Class_Globals
    Public Root As B4XView
    Public xui As XUI
 
    Public Storage As ExternalStorage
 
    Public ExternalAppPath As ExternalFile
    Public InternalAppPath As String
 
    Public ES_LibraryPath As ExternalFile
    Public ES_FilePath As ExternalFile
    Public ES_ImageFilePath As ExternalFile
 
    Public IS_LibraryPath As String
    Public IS_FilePath As String
    Public IS_ImageFilePath As String
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
 
    '--------------- INITIALIZE EXTERNAL STORAGE -----------------
 
    Storage.Initialize (Me, "Storage")
 
    Wait for (CheckExternalStoragePermission) Complete (result As Boolean)   ' If user already gave permission, then continue below...
 
    If result = False Then
        Log("ACCESS FILE PERMISSION DANIED")
        ToastMessageShow("ACCESS FILE PERMISSION DANIED", False)
        MsgboxAsync("ACCESS FILE PERMISSION DANIED. May you deleted the app folder ?" & CRLF & CRLF & "Uninstall the app and try again", " A T T E N T I O N")
        Wait For MsgBox_Result (result As Int)
        ExitApplication  ' Otherwise exit if no permission
    Else
        Log("ACCESS FILE PERMISSION ALLOWED. APP FOLDER: " & Storage.Root.Name)
        ToastMessageShow("ACCESS FILE PERMISSION ALLOWED: " & Storage.Root.Name, True)
    End If
 
    '---------------------------------------------
 
    ExternalAppPath = Storage.Root
    InternalAppPath = File.Combine(File.DirInternal, Storage.Root.Name)
 
    If Not(Storage.Exists(Storage.Root, "Library")) Then
        MessageBox("Successfully created a folder named """ & ExternalAppPath.Name & """ on your device internal memory,"DEAR USER")
    End If
 
    ' Folders on DirInternal
    IS_LibraryPath = File.Combine(InternalAppPath, "Library")
    IS_FilePath = File.Combine(InternalAppPath, "Files")
    IS_ImageFilePath = File.Combine(InternalAppPath, "Images")
    ' Find or create folders on external storage
    ES_LibraryPath = Storage.FindDirOrCreate(Storage.Root, "Library")
    ES_FilePath = Storage.FindDirOrCreate(Storage.Root, "Files")
    ES_ImageFilePath = Storage.FindDirOrCreate(Storage.Root, "Images")
 
    '---------- COPY SOME DEFAULT FILES TO EXTERNAL STORAGE SUBFOLDERS ----------
 
    ' NOTE: Storage.CopyFileToExternalStorage is a sub I wrote in ExternalStorage
    '       class. See my ExternalStorage Extras here:
    '       https://www.b4x.com/android/forum/threads/external-storage-extras.143576/#post-909906
    Dim Success As Boolean = False
    Dim FileName, FileNameLcase As String

    FileName = "VirtualDisplay_1.0.4.zip"
    FileNameLcase = FileName.ToLowerCase
    If Storage.Exists(ES_LibraryPath, FileName) = False Then
        Success = Storage.CopyFileToExternalStorage(File.DirAssets, FileNameLcase, ES_LibraryPath, FileName)
        If Success Then
            Log("Successfully created the file " & FileName)
        Else
            LogColor("Error while copy the file " & FileName, Colors.Red)
        End If
    End If
 
    FileName = "ESP8266-DARK.png"
    FileNameLcase = FileName.ToLowerCase
    If Storage.Exists(ES_ImageFilePath, FileName) = False Then
        Success = Storage.CopyFileToExternalStorage(File.DirAssets, FileNameLcase, ES_ImageFilePath, FileName)
        If Success Then
            Log("Successfully created the file " & FileName)
        Else
            LogColor("Error while copy the file " & FileName, Colors.Red)
        End If
    End If
 
    '-------- IMPORT ----------

    Dim Success As Boolean = CopyAllFoldersExternalToInternal(ExternalAppPath, InternalAppPath)
    If Success Then
        Log("SUCCESSFULLY IMPORTED FOLDER [" & ExternalAppPath.Name & "]")
        ToastMessageShow("IMPORT DONE", False)
    Else
        Log("CANNOT IMPORT FOLDER [" & ExternalAppPath.Name & "]")
        ToastMessageShow("IMPORT ERROR", False)
    End If
 
    Log("LIST FILES ON DIR INTERNAL AFTER IMPORT:")
    For Each ff As String In File.ListFiles(InternalAppPath)
        Log("Found on DirInternal: " & ff)
    Next
 
    '-------- EXPORT ----------  ' Can do the same to export

    ' Success = CopyAllFoldersInternalToExternal(InternalAppPath, ExternalAppPath)
    '...........

End Sub

Sub CheckExternalStoragePermission As ResumableSub
    If Not(Storage.HasPermission) Then
        MsgboxAsync("Due to Google Play Store security policy, we show you a file manager, please create a new folder, possibly named 'VirtualDisplay' to use as Application Folder and select it to grant access permissions." & CRLF & CRLF & _
        "Next you can use this folder to put/get files in created sub-folders.","Create application folder")
        Wait For MsgBox_Result (Result As Int)
    End If
 
    Storage.SelectDir(True)
    Wait For Storage_ExternalFolderAvailable

    Return (Storage.Root.IsFolder And Storage.Root.Name <> "")
End Sub

' IMPORT. Recursively copy all folders and files from ExternalStorage folder to DirInternal folder. (Mantain file structure)
Private Sub CopyAllFoldersExternalToInternal (Source As ExternalFile, targetFolder As String) As Boolean

    If Source.IsInitialized = False Or Source.IsFolder = False Then Return False

'    Log("COPY [" & Source.Name & "] CONTENT --> " & targetFolder)
 
    For Each f As ExternalFile In Storage.ListFiles(Source)
'        Log("File name: " & f.Name)
 
        If f.IsFolder Then
'            Log("TEST IF FOLDER EXISTS: " & File.Combine(targetFolder, f.Name))
            If File.Exists(targetFolder, f.Name) = False Then File.MakeDir(targetFolder, f.Name)  ' THANKS EREL
            CopyAllFoldersExternalToInternal (f, File.Combine(targetFolder, f.Name))  ' THANKS EREL
            Continue
        End If
 
        ' External to Internal
        Dim iStream As InputStream, oStream As OutputStream

        If f.IsInitialized = True And f.Length > 0 Then
            Log("IMPORT FILE: [" & f.Name & "]   -->  " & File.Combine(targetFolder, f.Name))
 
            If LogIsStarted Then
                LVLog.AddSingleLine("IMPORT FILE:  " & f.Name)
                LVLog.SetSelection(LVLog.Size-1)
            End If
 
            iStream = Storage.OpenInputstream(f)
            oStream = File.OpenOutput(targetFolder, f.Name, False)
            File.copy2(iStream, oStream)
            iStream.Close
            oStream.Close
        Else
            Log("Cannot open file from external storage")
            Return False
        End If
    Next
 
    Return True
End Sub

' EXPORT. Recursively copy all folders and files from DirInternal folder to ExternalStorage folder. (Mantain file structure)
Private Sub CopyAllFoldersInternalToExternal(Source As String, targetFolder As ExternalFile) As Boolean
   'NO YET WRITTEN'
End Sub
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Only ExternalStorare (Storage.Root) with grant permissions and DirInternal are involved, no DirRootExternal, so I think this shoud not a problem with Google, I'm wrong ?
I think that when access is grant on a folder, then I can do file operations on this folder and sub-folders by code without request other permissions to the user for any file, right?
As far as I understand Google policies, I think that you are correct.
 
Upvote 0
Top