0

i am trying to select an image from my gallery and then uploading to my server with a form-data request

this is how i start intent to select image

val intent = Intent()
        intent.type = "image/*"
        intent.action = Intent.ACTION_GET_CONTENT
        startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE)

and this is my onActivityResult

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode == SELECT_PICTURE && resultCode == RESULT_OK && data != null) {
        true -> {
            data.data?.let {
                teamImagePlaceholder?.setImageURI(it)
                it.path?.let { path ->
                    selectedImage = File(path)
                    selectedImage?.createNewFile()
                }
            }
        }
    }
}

the app then crashes at the line selectedImage?.createNewFile() and this is the stacktrace

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=66036, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/raw:/storage/emulated/0/Download/download.jpeg flg=0x1 }} to activity {.MainActivity}: java.io.IOException: No such file or directory
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4845)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
    at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
 Caused by: java.io.IOException: No such file or directory

and this is the retrofit method where i make the api call

fun createNewTeam(teamName: String, teamTarget: String, teamDescription: String, teamImage: String?,
                  successCallback: (CreateTeamResponse?) -> Unit, failureCallback: () -> Unit) {
    val name = MultipartBody.Part.createFormData("team_name", null, RequestBody.create(MediaType.parse("text/plain"), teamName))
    val target = MultipartBody.Part.createFormData("team_target", null, RequestBody.create(MediaType.parse("text/plain"), teamTarget))
    val description = MultipartBody.Part.createFormData("team_description", null, RequestBody.create(MediaType.parse("text/plain"), teamDescription))
    val image = when (teamImage) {
        null -> MultipartBody.Part.createFormData("team_image", "")
        else -> MultipartBody.Part.createFormData("team_image", teamImage, RequestBody.create(MediaType.parse("image/*"), teamImage))
    }

    client.createNewTeam(name, target, description, image).enqueue(object : Callback<CreateTeamResponse> {
        override fun onResponse(call: Call<CreateTeamResponse>, response: Response<CreateTeamResponse>) {
            successCallback(response.body())
        }

        override fun onFailure(call: Call<CreateTeamResponse>, t: Throwable) {
            failureCallback()
        }
    })
}

retrofit interface

@Multipart
@POST("api/apihere")
fun createNewTeam(@Part teamName: MultipartBody.Part, @Part teamTarget: MultipartBody.Part,
                  @Part teamDescription: MultipartBody.Part, @Part teamImage: MultipartBody.Part): Call<CreateTeamResponse>

EDIT

after following CommonWares answer on the other question this is my code now

repo

fun createNewTeam(teamName: String, teamTarget: String, teamDescription: String, teamImage: Uri?,
                  contentResolver: ContentResolver, successCallback: (CreateTeamResponse?) -> Unit, failureCallback: () -> Unit) {
    val builder = MultipartBody.Builder()
    builder.addFormDataPart("team_name", null, RequestBody.create(MediaType.parse("text/plain"), teamName))
    builder.addFormDataPart("team_target", null, RequestBody.create(MediaType.parse("text/plain"), teamTarget))
    builder.addFormDataPart("team_description", null, RequestBody.create(MediaType.parse("text/plain"), teamDescription))
    if(teamImage != null)
        builder.addFormDataPart("team_image", teamImage.toString(), InputStreamRequestBody(MediaType.parse("image/*")!!, contentResolver, teamImage))
    builder.setType(MultipartBody.FORM)
    val requestBody = builder.build()

    client.createNewTeam(requestBody).enqueue(object : Callback<CreateTeamResponse> {
        override fun onResponse(call: Call<CreateTeamResponse>, response: Response<CreateTeamResponse>) {
            successCallback(response.body())
        }

        override fun onFailure(call: Call<CreateTeamResponse>, t: Throwable) {
            failureCallback()
        }
    })
}

interface

@POST("api/apihere")
fun createNewTeam(@Body multipartBody: MultipartBody): Call<CreateTeamResponse>

i am still getting the same issue, request succeeds but the image is never uploaded

Mahmoud Omara
  • 533
  • 6
  • 22
  • Do not try to convert your obtained uri to a File class instance. Use the uri directly. – blackapps Jun 25 '20 at 14:54
  • .createNewFile(). Why would you create a new file? It would destroy the picked file if your File class had worked – blackapps Jun 25 '20 at 14:55
  • do you mean to pass the uri to my repository and call retrofit with uri directly? how do i parse the uri to MultipartBody.Part over there then? cause i did try this and although it did not crash my app, the image did not upload – Mahmoud Omara Jun 25 '20 at 14:55
  • Yes Retrofit can use that uri directly. Google for it. Your question is asked here every week for the last year. – blackapps Jun 25 '20 at 14:58
  • Use data.string instead of data.path. – blackapps Jun 25 '20 at 14:59
  • still same error, java.io.FileNotFoundException: content:/com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdownload.jpeg: open failed: ENOENT (No such file or directory) – Mahmoud Omara Jun 25 '20 at 15:06
  • Dont try to convert that uri -the content scheme- to a File class instance. Didnt i say so before? Use data.string for transferrerring the uri to Retrofit. – blackapps Jun 25 '20 at 15:30
  • well the api call succeeds but the image is not uploaded, it's null, i will update my question with the retrofit part – Mahmoud Omara Jun 25 '20 at 15:34
  • i have tried the first "duplicate" solution and it still gives the same result, the request succeeds without the image, while the 2nd "duplicate" is not my exact case since i do have other values that i need to send alongside the image – Mahmoud Omara Jun 25 '20 at 16:50
  • @CommonsWare the answers provided in the other questions are not what i'm looking for – Mahmoud Omara Jun 25 '20 at 16:51
  • The first duplicate points out an `InputStreamRequestBody` (second one in [this comment](https://github.com/square/okhttp/issues/3585#issuecomment-327319196)) that you can use to upload an image as part of a form submission when using OkHttp/Retrofit. If you are having problems with it, perhaps open a new question with a new [mcve] showing how you are implementing and using `InputStreamRequestBody`. The second duplicate is simply a Google engineer pointing out that your assumption that a `Uri` represents a file is flawed and that you need to use the `Uri` directly. – CommonsWare Jun 25 '20 at 17:49
  • check the edited section – Mahmoud Omara Jun 28 '20 at 20:15

0 Answers0