Getting error "open failed: EFAULT (Bad address)" when okhttp tries to send choosed from PhotoPicker image

huangapple go评论75阅读模式
英文:

Getting error "open failed: EFAULT (Bad address)" when okhttp tries to send choosed from PhotoPicker image

问题

这个错误只会在 Android 13 上从 Photo Picker 中选择图像时发生(在没有 Photo Picker 的版本上一切正常)。

代码功能如下:

用户从 Photo Picker 中选择图像。

获取图像的 URI 并将文件保存在主体中,一切正常:

File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri); 
postBody.setPhoto(imgFile);
@Throws(Exception::class)
fun getFileFromUri(context: Context, uri: Uri): File? {
    var path: String? = null
    if ("content".equals(uri.scheme, ignoreCase = true)) {
        path = getDataColumn(context, uri, null, null)
    } else if ("file".equals(uri.scheme, ignoreCase = true)) {
        path = uri.path
    }
    return if (!path.isNullOrEmpty()) File(path) else null
}

private fun getDataColumn(context: Context, uri: Uri, 
                          selection: String?, selectionArgs: Array<String?>?): String? {
    var cursor: Cursor? = null
    val column = MediaStore.Images.Media.DATA
    val projection = arrayOf(column)
    try {
        cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
        if (cursor != null && cursor.moveToFirst()) {
            val column_index = cursor.getColumnIndexOrThrow(column)
            return cursor.getString(column_index)
        }
    } finally {
        cursor?.close()
    }
    return null
}

但是当主体以多部分形式发送时:

final Map<String, RequestBody> multipart = new HashMap<>(2);
multipart.put("product",
            RequestBody.create(MediaType.parse("application/json"),
            BaseApplication.getGson().toJson(postBody)));
multipart.put("image\"; filename=\"%1.jpg\"",
            RequestBody.create(MediaType.parse("image/jpeg"),
            postBody.getPhoto())); // type File
....  
serverApi.sendBody(multipart); 
....

在 logCat 中会显示警告:

/sdcard/.transforms/synthetic/picker/0/com.android.providers.media.photopicker/media/1000000019.jpg: open failed: EFAULT (Bad address)

然后 Retrofit/OkHttp 会抛出异常。

Retrofit 版本为 "com.squareup.retrofit2:retrofit:2.9.0"。

在选择图像之前在 AndroidManifest 中添加的权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 13 权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>

还在 <application> 中添加了:

android:requestLegacyExternalStorage="true"

请求权限的代码:

if (VERSION.SDK_INT >= 33) arrayOf(Manifest.permission.READ_MEDIA_IMAGES) else arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)

是否可以在不使用 MANAGE_EXTERNAL_STORAGE 权限的情况下获取并发送该图像?因为应用程序通过 Google Play 发布,不是文件管理器之类的应用。也许我应该将文件复制到应用程序的内部文件目录中,或者还有其他方法吗?

英文:

This error occurs ONLY when an image was choosed from Photo Picker on android 13 (on versions without Photo Picker all works!).

What does code:

User picks image from Photo Picker.

Get image's uri and save file in body, there all is working too:

File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri); 
postBody.setPhoto(imgFile);
@Throws(Exception::class)
fun getFileFromUri(context: Context, uri: Uri): File? {
    var path: String? = null
    if (&quot;content&quot;.equals(uri.scheme, ignoreCase = true)) {
        path = getDataColumn(context, uri, null, null)
    } else if (&quot;file&quot;.equals(uri.scheme, ignoreCase = true)) {
        path = uri.path
    }
    return if (!path.isNullOrEmpty()) File(path) else null
}

private fun getDataColumn(context: Context, uri: Uri, 
                          selection: String?, selectionArgs: Array&lt;String?&gt;?): String? {
    var cursor: Cursor? = null
    val column = MediaStore.Images.Media.DATA
    val projection = arrayOf(column)
    try {
        cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
        if (cursor != null &amp;&amp; cursor.moveToFirst()) {
            val column_index = cursor.getColumnIndexOrThrow(column)
            return cursor.getString(column_index)
        }
    } finally {
        cursor?.close()
    }
    return null
}

But when body is sending as multipart,

final Map&lt;String, RequestBody&gt; multipart = new HashMap&lt;&gt;(2);
multipart.put(&quot;product&quot;,
            RequestBody.create(MediaType.parse(&quot;application/json&quot;),
            BaseApplication.getGson().toJson(postBody)));
multipart.put(&quot;image\&quot;; filename=\&quot;%1.jpg\&quot;&quot;,
            RequestBody.create(MediaType.parse(&quot;image/jpeg&quot;),
            postBody.getPhoto())); // type File
....  
serverApi.sendBody(multipart); 
....

a warning was shown in logCat:

*
/sdcard/.transforms/synthetic/picker/0/com.android.providers.media.photopicker/media/1000000019.jpg: open failed: EFAULT (Bad address)*

and then retrofit/okhttp throws exception.

Retrofit version "com.squareup.retrofit2:retrofit:2.9.0"

Permissions in AndroidManifest, which are given before picking image:

&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot; /&gt;
&lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot; /&gt;
&lt;!--Android 13 permissions--&gt;
&lt;uses-permission android:name=&quot;android.permission.READ_MEDIA_IMAGES&quot;/&gt;

And
android:requestLegacyExternalStorage=&quot;true&quot;
in &lt;application&gt; too.

Asking permissions:

if (VERSION.SDK_INT &gt;= 33) arrayOf(Manifest.permission.READ_MEDIA_IMAGES) else arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)

Can I get and send that image without using MANAGE_EXTERNAL_STORAGE permission? Because application distributes via Google Play and is not files manager or something like that.
Maybe I should copy file in app's internal files dir or what?

答案1

得分: 0

私有方法:grant read uri权限

  private void onImagePicked(Uri imgUri){
    if(imgUri!=null) {
      try {

        grantUriPermission(getPackageName(),imgUri,Intent.FLAG_GRANT_READ_URI_PERMISSION);

        File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri);
        postBody.setPhoto(imgFile);
      } catch (Exception e) {
        ...
      }
    }
  }
英文:

Well, found solution: grant read uri permission

  private void onImagePicked(Uri imgUri){
    if(imgUri!=null) {
      try {

        grantUriPermission(getPackageName(),imgUri,Intent.FLAG_GRANT_READ_URI_PERMISSION);

        File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri);
        postBody.setPhoto(imgFile);
      } catch (Exception e) {
        ...
      }
    }
  }

huangapple
  • 本文由 发表于 2023年7月6日 16:52:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76627095.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定