Search code examples
androidkotlinfirebase-storageandroid-contentresolvermediastore

How to download a media file from FirebaseStorage to local storage using MediaStore?


I was building an android app, for which I had to download media from FirebaseStorage to local storage using MediaStore for AndroidQ and above. For that I wrote the code below referring to this and this.

@RequiresApi(Build.VERSION_CODES.Q)
suspend fun downloadMedia(model: Message): Uri {
    Log.d(TAG, "downloadMedia: Q")
    val fileName = "SK ${model.timeStamp}.jpg"
    val dirPath = "${Environment.DIRECTORY_PICTURES}/Skara"
    val mimeType = "image/jpg"
    val collectionUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)

    val values = ContentValues().apply {
        put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
        put(MediaStore.Video.Media.RELATIVE_PATH, dirPath)
        put(MediaStore.Video.Media.MIME_TYPE, mimeType)
        put(MediaStore.Video.Media.IS_PENDING, 1)
    }

    val uri = contentResolver.insert(collectionUri, values)!!
    try {
        Firebase.storage.getReferenceFromUrl(model.mediaUrl).getFile(uri).await()
    } catch (e: StorageException) {
        Log.d(TAG, "downloadMedia: StorageException")
        Log.d(TAG, "downloadMedia: $uri")
        Log.d(TAG, "downloadMedia: ${e.message}")
        Log.d(TAG, "downloadMedia: ${e.cause}")
    }
    values.apply {
        clear()
        put(MediaStore.Video.Media.IS_PENDING, 0)
    }
    contentResolver.update(uri, values, null, null)
    return uri
}

But this code logged the following error.

2020-12-08 15:18:38.602 8124-8201/com.skb.skara D/MessageActivity: downloadMedia: Q
2020-12-08 15:18:39.872 8124-8201/com.skb.skara D/MessageActivity: downloadMedia: StorageException
2020-12-08 15:18:39.876 8124-8201/com.skb.skara D/MessageActivity: downloadMedia: content://media/external/images/media/680
2020-12-08 15:18:39.879 8124-8201/com.skb.skara D/MessageActivity: downloadMedia: An unknown error occurred, please check the HTTP result code and inner exception for server response.
2020-12-08 15:18:39.881 8124-8201/com.skb.skara D/MessageActivity: downloadMedia: java.io.IOException: No such file or directory

I don't know what is the cause of error and how to resolve it. Please help me. I have READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. It is also creating 'Skara' folder inside the 'Pictures' folder, but the folder is empty.


Solution

  • Finally I have found out some working code for this. (If anyone knows a better alternative, please answer).

    @RequiresApi(Build.VERSION_CODES.Q)
    suspend fun downloadMedia(model: Message): Uri {
        val fileName = "SK ${model.timeStamp}.jpg"
        val dirPath = "${Environment.DIRECTORY_PICTURES}/Skara"
        val mimeType = "image/jpg"
        val collectionUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
    
        val values = ContentValues().apply {
            put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
            put(MediaStore.Video.Media.RELATIVE_PATH, dirPath)
            put(MediaStore.Video.Media.MIME_TYPE, mimeType)
            put(MediaStore.Video.Media.IS_PENDING, 1)  // This indicates that, the file is not available for other processes until you reset it back to 0.
        }
    
        val uri = contentResolver.insert(collectionUri, values)!!
            contentResolver.openOutputStream(uri)?.use { ops ->
                Firebase.storage.getReferenceFromUrl(model.mediaUrl).stream.await().stream.use { ips ->
                    val buffer = ByteArray(1024)
                    while (true) {
                        val bytes = ips.read(buffer)
                        if (bytes == -1)
                            break
                        ops.write(buffer, 0, bytes)
                    }
                    // The above seven lines can be written in a single line as below. (from 'val buffer' to '}')
                    // ips.copyTo(ops) - Kotlin extension function
                }
            }
    
        values.apply {
            clear()
            put(MediaStore.Video.Media.IS_PENDING, 0)  // Resetting back to zero after the download completes.
        }
        contentResolver.update(uri, values, null, null)
        return uri
    }
    

    Still, I have queries with this answer, which I have asked here.