作者:Angki
轉載請註明
最近發現項目在Android 10下,上傳圖片報錯
open failed: EACCES (Permission denied)
上網查了下,是由於Android 10的文件存儲機制修改成了沙盒模式,應用不能直接訪問除了沙盒文件和公共文件以外的文件,直接使用圖片絕對地址上傳圖片會出錯。最簡單的解決辦法就是在AndroidManifest.xml中添加
android:requestLegacyExternalStorage="true"
但是,這方式只是臨時解決而已,Google也說明了在Android 11 中這種臨時的解決辦法也會失效。爲了以後方便,直接就是用Google推薦的方式進行適配。
由於項目網絡框架使用OkHttp4和Retrofit,只要把上傳需要的MultipartBody對象構建出來就行了,之前的寫法:
val file = File(圖片的絕對地址)
//傳輸圖片
val img = file.asRequestBody("image/png".toMediaType())
//MultipartBody
val request = MultipartBody.Builder()
.addFormDataPart("file", "${TimeUtils.getNowMills()}.png", img)
.build()
適配後的寫法
var file: ByteArray? = null
contentResolver.openInputStream(圖片的Uri).use {
file = it?.readBytes()
}
file?.let {
val img = it.toRequestBody("image/png".toMediaType())
val request = MultipartBody.Builder()
.addFormDataPart("file", "${TimeUtils.getNowMills()}.png", img)
.build()
mViewModel.uploadImage(request)
}
之前的寫法和適配後的寫法差別就是在於RequestBody的獲取。在適配過程中,陷入了一個誤區,就是要先獲取File文件,然後使用OkHttp提供的方法來獲取RequestBody。
fun File.asRequestBody(contentType: MediaType? = null): RequestBody
然後就找方法來獲取File,但是只找到了通過Uri獲取Bitmap,我就在想着先獲取Bitmap,然後把Bitmap轉換成File文件。幸運的是,我看見了OkHttp提供的其他方法來獲取RequestBody。
fun String.toRequestBody(contentType: MediaType? = null): RequestBody
fun ByteString.toRequestBody(contentType: MediaType? = null): RequestBody
fun ByteArray.toRequestBody(
contentType: MediaType? = null,
offset: Int = 0,
byteCount: Int = size
): RequestBody
再結合在官網看見的獲取文件的流方法:
// Open a specific media item using InputStream.
val resolver = applicationContext.contentResolver
resolver.openInputStream(content-uri).use { stream ->
// Perform operations on "stream".
}
最後就有了適配的寫法,測試了一下,沒得問題。最後得出一個結論,我是菜狗…