Retrofit File Upload using FileProvider ( for Content URIs )

Uploading a file that is referenced from File class is pretty straight forward. Let us see how to perform a Retrofit file upload using FileProvider ( or Content URI ).

With Android 11 it is not possible to use the android:requestLegacyExternalStorage="true" to get files directly from internal / external storage. Hence, this tutorial for you people.

This project assumes you already are familiar with Retrofit. If you don’t know retrofit, then you should read my earlier tutorial about retrofit: Learn Retrofit in Android GET with Example.

Agenda

Let us first discuss what we are going to do for Retrofit file upload using FileProvider. It’s pretty straight forward and not so involved.

Here are the steps we need to undertake for us to actually upload file.

  • Get the file name and file type
  • Get the actual size of the file to be uploaded
  • Get the mime type of the file
  • Upload the file to the server

Those are the tasks that we need to uploading files with Retrofit and FileProvider’s Content URIs.

Overriding RequestBody class

First create a class named ContentUriRequestBody and fill it with content below.

class ContentUriRequestBody( private val contentResolver: ContentResolver, private val contentUri: Uri ) : RequestBody() { override fun contentType(): MediaType? { val contentType = contentResolver.getType(contentUri) return contentType?.toMediaTypeOrNull() } override fun contentLength(): Long { val size = contentUri.length(contentResolver) return size } override fun writeTo(bufferedSink: BufferedSink) { val inputStream = contentResolver.openInputStream(contentUri) ?: throw IOException("Couldn't open content URI for reading") inputStream.source().use { source -> bufferedSink.writeAll(source) } } }
Code language: Kotlin (kotlin)

By this point you must have a working code with a little error at contentUri.length(contentResolver). Let us solve that too.

Create extension function to calculate RequestBody length

Just add the code below in the same file. It’s just an extension function so it doesn’t really matter.

fun Uri.length(contentResolver: ContentResolver) : Long { val assetFileDescriptor = try { contentResolver.openAssetFileDescriptor(this, "r") } catch (e: FileNotFoundException) { null } // uses ParcelFileDescriptor#getStatSize underneath if failed val length = assetFileDescriptor?.use { it.length } ?: -1L if (length != -1L) { return length } // if "content://" uri scheme, try contentResolver table if (scheme.equals(ContentResolver.SCHEME_CONTENT)) { return contentResolver.query(this, arrayOf(OpenableColumns.SIZE), null, null, null) ?.use { cursor -> // maybe shouldn't trust ContentResolver for size: https://stackoverflow.com/questions/48302972/content-resolver-returns-wrong-size val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) if (sizeIndex == -1) { return@use -1L } cursor.moveToFirst() return try { cursor.getLong(sizeIndex) } catch (_: Throwable) { -1L } } ?: -1L } else { return -1L } }
Code language: Kotlin (kotlin)

Now with that in place you are ready to upload your files up in the cloud. Let us see how we can use the code above.

Using with Retrofit

Let us see a snippet that shows us how we can use it.

val body = MultipartBody.Builder() body.addFormDataPart( "file", "test.$extension", ContentUriRequestBody(App.getThis().contentResolver, uri) ) val bodyTosend = body.build() // send this object to your retrofit instance
Code language: Kotlin (kotlin)

That’s it there’s no more anything that needs to be done. That concludes our Retrofit file upload using FileProvider, keep learning.

Suggested

Android Intent: Learn Implicit Intent and Explicit Intent

Android Notification Manager: Create Notification in Android

Android RecyclerView: How to Insert, Update and Delete Item

Run Length Encoding in Java: Learn How To Implement

Related Posts