英文:
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 ("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
}
But when body is sending as multipart,
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);
....
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:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--Android 13 permissions-->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
And
android:requestLegacyExternalStorage="true"
in <application> too.
Asking permissions:
if (VERSION.SDK_INT >= 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) {
...
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论