square / retrofit

A type-safe HTTP client for Android and the JVM

Home Page:https://square.github.io/retrofit/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Image uploaded to server is corrupted when using multipart

vraj-s-shah opened this issue · comments

Hi there. I am trying to upload an image to the server using multipart and facing one issue. The upload request is proper and I am getting a response of 200 for success and receiving an ID of the uploaded media. When I try to download the media from the same ID I received, the file is downloaded but it is corrupted and can't be opened. Can you please help?
We have our server over Matrix server.

AppModule code where retrofit instance is created. Here MatrixAuthInterceptor will add a header for authorization.

@Singleton
@Provides
@Named(OKHTTP_MATRIX_CLIENT)
fun provideOkHttpMatrixClient(
    matrixAuthInterceptor: MatrixAuthInterceptor
): OkHttpClient = OkHttpClient.Builder()
    .addInterceptor(matrixAuthInterceptor)
    .build()

@Singleton
@Provides
fun provideMatrixService(
    @Named(OKHTTP_MATRIX_CLIENT) okHttpClient: OkHttpClient,
    @Named(MATRIX_SERVICE_RESULT_CALL_ADAPTER_FACTORY) factory: ApiResultCallAdapterFactory
): MatrixService = Retrofit.Builder()
    .baseUrl(Urls.Matrix.BASE_URL)
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(factory)
    .build()
    .create(MatrixService::class.java)

Here is my MatrixService.kt

interface MatrixService {

    @Multipart
    @POST(UPLOAD_MEDIA)
    suspend fun uploadFile(
        @Part request: MultipartBody.Part,
        @Query("filename") filename: String
    ): ApiResult<UploadFileResponse>
}

And this is my repository which provides upload functionality to app.

interface MatrixServiceRepositoryDelegate {

    suspend fun uploadFile(
        file: File,
        filename: String,
        mimeType: String
    ): ApiResult<UploadFileResponse>
}

class MatrixServiceRepositoryDelegateImpl @Inject constructor(
    private val matrixService: MatrixService,
    @ApplicationContext private val context: Context
) : MatrixServiceRepositoryDelegate {

    override suspend fun uploadFile(
        file: File,
        filename: String,
        mimeType: String
    ): ApiResult<UploadFileResponse> {
        val request = MultipartBody.Part.createFormData(
            name = filename.getNameOfFileFromFullName(context),
            filename = filename,
            body = file.asRequestBody(mimeType.toMediaTypeOrNull())
        )
        return matrixService.uploadFile(request, filename)
    }
}

One thing to note here, I am using ActivityResultContracts.OpenDocument() for selecting a file and then creating a new file from received Uri before calling the upload function. Reference(here copyTo() is an extension function which copies the content of Uri to the given file):

private fun Uri.getFile(): File? {
    val picturesDirectory =
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
    val rootFolder = File(picturesDirectory, context.getString(R.string.app_name)).apply {
        mkdir()
    }
    val requiredFile = File(rootFolder, getFileName(context))
    return if (copyTo(context, requiredFile) < 0) null else requiredFile
}

PS: I have tested, filename and mime-types I am getting from the URI are proper.

Fixed the issue, I had to pass header Content-Type: application/octet-stream, remove MultiPart and pass body as in RequestBody instead of Mulitpart.Part.