Android Question Copy file from external storage (SD Card) failed

Wosl

Member
Dear All,

I ran in an issue with the external file handling (SD Card). The copy procedure from external device to File.DirDefaultExternal works but the copied (a bitmap) file is not complete. In other words the file is only partially copied.
I created a simple example with one map tile stored on File.DirDefaultExternal and on the external device. I used the file handling method for external devices and the file could be found and the copy process started.

Copy from SD Card:
Sub Globals
    Public xui As XUI
    Public Storage As ExternalStorage, lFile As ExternalFile
    Public PersistantUri As String
    Public ZoomImageView1 As ScaleImageView, ZoomImageView2 As ScaleImageView
    Public pnlView As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
'######################################################################
'
'  Run example with same tile loaded from different devices
'
'  Load tile form File.DirDefaultExternal and from SDcard
'
'######################################################################
    Private jRectLeft As Int, jRectTop As Int, jRectWidth As Int, jRectHeight As Int
    Private iZ As Int = 13, iX As Int = 4400, iY As Int = 2686
    Private cFileName As String
    Private Outputstream1 As OutputStream
    Private InputStream1 As InputStream
    Private bmp As B4XBitmap

    Log ("Run example with same tile loaded from different devices:")
    Log ("Load tile form File.DirDefaultExternal and from SDcard")
    Log (File.DirDefaultExternal)

    jRectLeft = 0dip
    jRectTop = 0dip
    jRectWidth = Activity.Width
    jRectHeight = Activity.Width / 2
    pnlView.Initialize("")
    Activity.AddView(pnlView, jRectLeft, jRectTop, jRectWidth, jRectHeight)
    pnlView.Color=Colors.Magenta
    
'  Tile 1: loaded from DirDefaultExternal
    jRectLeft = 0dip
    jRectTop = 0dip
    jRectWidth = Activity.Width / 2
    jRectHeight = Activity.Width / 2
    ZoomImageView1.Initialize("")
    pnlView.AddView(ZoomImageView1, jRectLeft, jRectTop, jRectWidth, jRectHeight)

'  Tile 2: loaded from SDCard
    jRectLeft = Activity.Width / 2
    ZoomImageView2.Initialize("")
    pnlView.AddView(ZoomImageView2, jRectLeft, jRectTop, jRectWidth, jRectHeight)

'  Tile 1
    cFileName = "Tile1_" & iZ & "_" & iX & "_" & iY & ".png"
    bmp = xui.LoadBitmap(File.DirDefaultExternal, cFileName)
    ZoomImageView1.Image = bmp

'  Tile 2
    Storage.Initialize (Me, "Storage")
    Storage.SelectDir(True)        ' set to False to define persistant Uri first time
    Wait For Storage_ExternalFolderAvailable
    cFileName = "Tile2_" & iZ & "_" & iX & "_" & iY & ".png"
    lFile = Storage.FindFile(Storage.root, cFileName)
    If lFile.IsInitialized = True And lFile.Length > 0 Then
        InputStream1 = Storage.OpenInputstream(lFile)
        Outputstream1 = File.OpenOutput(File.DirDefaultExternal, cFileName, False)
        File.copy2(InputStream1,Outputstream1)
        bmp = xui.LoadBitmap(File.DirDefaultExternal, cFileName)
        Outputstream1.Close
        ZoomImageView2.Image = bmp
    Else
        Log("File not found on external device")
    End If
    
    Log ("All done")
End Sub

And the result is as following:
1659372795142.png

A portion is missing of Tile2 (copied from external device). This happens if you select an other tile, too. The missing piece is sometimes smaller and larger but it always happens.

May be i missed something essential. Could somebody have a look please.

Wosl
 

Wosl

Member
Dear All!

Another modification solved the issue! I missed to close the inputstream. By adding the Inputstream1.Close statement right after File.copy2 and moving the Outputstream1.Close just behind it the world seems to be perfect now.

The code segment looks now as following:
B4X:
    cFileName2 = "Tile2_" & iZ & "_" & iX & "_" & iY & ".png"
    lFile = Storage.FindFile(Storage.root, cFileName2)
    If lFile.IsInitialized = True And lFile.Length > 0 Then
        InputStream1 = Storage.OpenInputstream(lFile)
        Outputstream1 = File.OpenOutput(File.DirDefaultExternal, cFileName2, False)
        File.copy2(InputStream1,Outputstream1)
        InputStream1.Close
        Outputstream1.Close
    Else
        Log("File not found on external device")
        Return
    End If
    bmp = xui.LoadBitmap(File.DirDefaultExternal, cFileName2)
    ZoomImageView2.Image = bmp

My assumption is that the input/output streams are using internal buffers to store the data. With the closing statement the buffers seem to be cleared and all data are copied as expected. If you have another explanation please let me know.

I hope some other users can participate on this learning.

Best Regards
Wosl
 
Upvote 0

max123

Active Member
Licensed User
Longtime User
On other enviroments there is a File.Flush() that forces complete read/write operations without necessary close the file.
Here you have to close the streams, I do not see Flush method.

PS Your code helped me, I need to do the inverse, copy files from DirAssets to ExternalStorage, this is my code not yet tested, I used File.Copy2 but not sure if it is the right option. Any suggestions ?

When I swicthed from default File to External Storage I had a lots of problems converting my code, need to write a class to use as bridge from each to other, possibly to use File class methods:
B4X:
        Dim FileName As String = "Cube20x20_Wall.gcode"
        Dim iStream As InputStream, oStream As OutputStream
        Dim lFile As ExternalFile = Storage.FindFileOrCreate(Storage.root, FileName)
        If lFile.IsInitialized = True Then
            iStream = File.OpenInput(File.DirAssets, FileName)
            oStream = Storage.OpenOutputStream(lFile)
            File.copy2(iStream, oStream)
            iStream.Close
            oStream.Close
        Else
            Log("Cannot create file on External Storage")
            Return
        End If
 
Last edited:
Upvote 0
Top