Android 10 使用OkHttp4和Retrofit上傳圖片

作者: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".
}

最後就有了適配的寫法,測試了一下,沒得問題。最後得出一個結論,我是菜狗…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章