0

I have the following function to select an image and display it in an ImageView (only relevant code):

Start gallery picker:

 fun selectPic(view: View) {
    val intent = Intent(Intent.ACTION_PICK);
    intent.type = "image/*";
    startActivityForResult(intent, 1000);
}

Get selected image from gallery picker:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK && requestCode == 1000) {
            println("DATA " + data?.data)

            // SET PATH TO GLOBAL VARIABLE
            uri_path = data?.data;

            findViewById<ImageView>(R.id.selected_image).setImageURI(data?.data);
        }
}

Then I use this piece of code to retrieve the image again so I can upload the file to a server:

...
 val file: File = File(getRealPathFromURI(fileUri));
...

getRealPathFromUri()

 fun getRealPathFromURI(uri: Uri?): String? {
        val cursor: Cursor? = contentResolver.query(uri!!, null, null, null, null)

        if (cursor == null) {
            return null;
        }

        cursor.moveToFirst()
        val idx: Int = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
        return cursor.getString(idx)
    }

uploadFile

private fun uploadFile(fileUri: Uri) {
        // create upload service client
        val service: FileUploadService =
            ServiceGenerator.createService(FileUploadService::class.java)

        // uri_path IS THE GLOBAL VARIABLE WITH VALUE data?.data
        val file: File = File(uri_path.toString());

        // create RequestBody instance from file
        val requestFile = RequestBody.create(
            MediaType.parse(contentResolver.getType(fileUri)),
            file
        )

        // MultipartBody.Part is used to send also the actual file name
        val body =
            MultipartBody.Part.createFormData("file", file.name, requestFile)

        // add another part within the multipart request
        val descriptionString = "hello, this is description speaking"
        val description = RequestBody.create(
            MultipartBody.FORM, descriptionString
        )

        // finally, execute the request
        val call: Call<ResponseBody?>? = service.upload(description, body)
        if (call != null) {
            call.enqueue(object : Callback<ResponseBody?> {
                override fun onResponse(
                        call: Call<ResponseBody?>?,
                        response: Response<ResponseBody?>?
                ) {
                    Log.v("Upload", "success")
                    println("RESPONSE " + response);
                }

                override fun onFailure(call: Call<ResponseBody?>?, t: Throwable) {
                    t.message?.let { Log.e("Upload error:", it) }
                }
            })
        }
    }

Relevant parts of my AndroidManifext.xml:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="ANDROID.PERMISSION.WRITE_EXTERNAL_STORAGE"/>
    <application

        ...
        android:usesCleartextTraffic="true"
        android:requestLegacyExternalStorage="true">
        ...

The weird issue is that it works for some files. Example of file that works:

I/System.out: URI content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F64/ORIGINAL/NONE/image%2Fjpeg/1462875550

This file works perfectly and uploads!

But this file gives an error:

E/Upload error:: /storage/emulated/0/Pictures/IMG_20210518_151857.jpg: open failed: EACCES (Permission denied)

Error: open failed: EACCES (Permission denied).

Why is the permission denied for this file? I do have the correct requirements in my AndroidManifext.xml?

O'Niel
  • 1,622
  • 1
  • 17
  • 35
  • `uri_path = data?.data;` You should also use that uri to upload the file. Dont try to get 'a real path from an uri`. Throw away that function. Use the uri directly. – blackapps May 25 '21 at 09:49
  • @blackapps That gives: `E/Upload error:: content:/com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F64/ORIGINAL/NONE/image%2Fjpeg/1577117090: open failed: ENOENT (No such file or directory)` also for the working file. – O'Niel May 25 '21 at 10:16
  • I see no code where you try to upload the file using that uri. – blackapps May 25 '21 at 10:17
  • @blackapps Question updated. – O'Niel May 25 '21 at 10:20
  • `val file: File = File(uri_path.toString());` You cannot use the File class for an Uri. Google for InputStreamRequestBody to see how to use an Uri. – blackapps May 25 '21 at 10:26
  • @blackapps Fixed. This is the correct way: `val inputStream: InputStream? = uri_path?.let { contentResolver.openInputStream(it) }` and then `inputStream?.readBytes()`. If you can type it out in an answer I'll accept it. – O'Niel May 25 '21 at 10:37
  • You clearly did not google for InputStreamRequestBody. – blackapps May 25 '21 at 10:39
  • @blackapps I did, and it's working perfectly. What is the problem? – O'Niel May 25 '21 at 10:40
  • The problem would be that you are loading the file in memory in a byte array. For very big files your device could not have enough memory for that action. The solution i proposed does not load that file in memory first. – blackapps May 25 '21 at 10:42
  • in ANDROID.PERMISSION.READ_EXTERNAL_STORAGE and ANDROID.PERMISSION.WRITE_EXTERNAL_STORAGE ANDROID.PERMISSION part has to be lowercase – Alexander Mokin May 26 '21 at 10:06

1 Answers1

-1

From Android 10 you can not access storage with help of Environment.getExternalStorageDirectory().toString().

Above path is deprecate you can use activity.baseContext?.cacheDir or activity.baseContext?.filesDir to store and get data.

webaddicted
  • 1,071
  • 10
  • 23