Kotlin如何将本地相册图片以string($binary)类型上传至服务器
Fragment实现相册图片上传对接Swagger二进制参数接口
先明确核心概念:Swagger文档标注的
string($binary)参数,本质要求接收multipart表单格式的原始文件二进制流,不是Base64编码字符串,也不是文件路径字符串。你之前搜到的Activity实现教程逻辑完全可以复用,Fragment和Activity实现的唯一差异是权限申请、系统组件回调的托管主体,不用特意找Fragment专属教程。
前置配置
- 模块级
build.gradle引入必要依赖,替换已废弃的onActivityResult回调:
implementation "androidx.activity:activity-ktx:1.8.2" implementation "androidx.fragment:fragment-ktx:1.6.2" // 网络请求依赖(Retrofit+OkHttp,自带multipart表单支持) implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation "com.squareup.okhttp3:okhttp:4.12.0"
AndroidManifest.xml添加对应相册权限,做版本适配:
<!-- Android 13及以上读图片权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Android 12及以下读存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
Fragment侧选图实现
直接在Fragment内部注册Activity Result回调,不需要依附Activity处理结果,避免生命周期错位:
class ImageUploadFragment : Fragment() { private val viewModel: UploadViewModel by viewModels() // 注册选图结果回调 private val pickImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { imageUri -> imageUri ?: return@registerForActivityResult // 拿到图片Uri后交给ViewModel处理上传逻辑 viewModel.uploadBinaryImage(imageUri, requireContext().contentResolver) } // 注册权限申请回调 private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> if (granted) pickImageLauncher.launch("image/*") else Toast.makeText(requireContext(), "请授予相册权限后再选图", Toast.LENGTH_SHORT).show() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 选图按钮点击事件 binding.btnPickImage.setOnClickListener { checkPermissionAndPick() } // 监听上传结果 viewModel.uploadResult.observe(viewLifecycleOwner) { result -> result.onSuccess { Toast.makeText(requireContext(), "上传成功", Toast.LENGTH_SHORT).show() }.onFailure { Toast.makeText(requireContext(), "上传失败:${it.message}", Toast.LENGTH_SHORT).show() } } } private fun checkPermissionAndPick() { val targetPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { Manifest.permission.READ_MEDIA_IMAGES } else { Manifest.permission.READ_EXTERNAL_STORAGE } if (ContextCompat.checkSelfPermission(requireContext(), targetPermission) == PackageManager.PERMISSION_GRANTED) { pickImageLauncher.launch("image/*") } else { permissionLauncher.launch(targetPermission) } } }
ViewModel侧上传逻辑实现
不要做多余的Base64转码,直接将图片流封装为Multipart类型传输即可:
class UploadViewModel : ViewModel() { private val _uploadResult = MutableLiveData<Result<Unit>>() val uploadResult: LiveData<Result<Unit>> = _uploadResult fun uploadBinaryImage(imageUri: Uri, contentResolver: ContentResolver) { viewModelScope.launch(Dispatchers.IO) { var inputStream: InputStream? = null try { inputStream = contentResolver.openInputStream(imageUri) val imageBytes = inputStream?.readBytes() ?: throw IllegalStateException("图片读取失败") // 构造二进制请求体,MIME类型和选图时保持一致 val requestBody = imageBytes.toRequestBody("image/*".toMediaTypeOrNull()) // 构造Multipart表单参数,注意第一个参数必须和Swagger文档里的参数名完全一致 val imagePart = MultipartBody.Part.createFormData( name = "file", // 替换成你自己接口里的参数名 filename = "upload_${System.currentTimeMillis()}.jpg", body = requestBody ) // 发起请求,Retrofit接口定义参考如下注释 /* interface UploadApi { @Multipart @POST("你的接口地址") suspend fun uploadImage(@Part file: MultipartBody.Part): Response<BaseResponse<Any>> } */ val response = NetworkModule.uploadApi.uploadImage(imagePart) if (response.isSuccessful && response.body()?.code == 200) { _uploadResult.postValue(Result.success(Unit)) } else { _uploadResult.postValue(Result.failure(Exception("服务端返回错误:${response.code()}"))) } } catch (e: Exception) { _uploadResult.postValue(Result.failure(e)) } finally { inputStream?.close() } } } }
高频踩坑点
- 禁止将图片转Base64字符串传输,会直接触发参数类型不匹配错误,
$binary要求原始二进制流 - 不要在Activity上注册Fragment用到的选图、权限申请回调,必须在Fragment自身注册,否则会出现回调丢失、内存泄漏问题
createFormData传入的name字段必须和Swagger文档标注的参数名完全一致,否则后端无法接收文件- 图片流读取、网络请求必须放在子线程执行,不要在主线程操作IO,否则会触发ANR
内容的提问来源于stack exchange,提问作者uul
相关产品推荐
相关产品推荐

