You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin如何使用Retrofit将本地路径图片上传到服务端

Android Kotlin Retrofit 本地路径图片上传完整实现方案

1. 修改Retrofit API接口

原有接口缺少图片上传对应的Part参数,调整为如下代码:

@Multipart
@POST("add_passport.php")
suspend fun addPassport(
    @Part("userid") userid: String,
    @Part("fistname") fistname:String,
    @Part("surname") surname:String,
    @Part("nationality") nationality:String,
    @Part("dof") dof:String,
    @Part("gender") gender:String,
    @Part("age") age:String,
    @Part("sig") sig: String,
    @Part("salt") salt: String,
    // 新增图片参数,注意part的名称要和服务端接收的字段名完全一致,此处示例为passport_img
    @Part imagePart: MultipartBody.Part
):Response<PassportInsertApiClass>

2. 新增本地文件转MultipartBody.Part工具方法

写通用工具函数,将本地图片路径转换为Retrofit上传需要的MultipartBody.Part对象:

fun getImageMultipart(imagePath: String, fieldName: String = "passport_img"): MultipartBody.Part? {
    val file = File(imagePath)
    if (!file.exists()) return null
    // 构造RequestBody,指定媒体类型为图片
    val requestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
    // 封装为MultipartBody.Part,第二个参数为文件名,可自定义
    return MultipartBody.Part.createFormData(fieldName, file.name, requestBody)
}

注意:fieldName参数值必须和服务端接收图片的字段名完全一致,比如服务端PHP用$_FILES['passport_img']接收,此处就传passport_img。

3. 调整Repository层代码

修改原有Repository逻辑,将传入的图片路径转换为Part后传给API接口:

// 原有image参数改为String类型的imagePath,存储本地图片路径
suspend fun addPassport(
    userid: String,
    fistname:String,
    surname:String,
    nationality:String,
    dof:String,
    gender:String,
    age:String,
    sig:String,
    salt:String,
    imagePath:String
):Response<PassportInsertApiClass>{
    val imagePart = getImageMultipart(imagePath) ?: throw IllegalArgumentException("图片文件不存在")
    return RetrofitInstance.api.addPassport(userid,fistname,surname,nationality,dof,gender,age,sig,salt,imagePart)
}

4. 调整ViewModel层代码

同步修改ViewModel的参数类型,传入图片路径即可,新增异常捕获逻辑:

fun addPassport(
    userid: String,
    fistname:String,
    surname:String,
    nationality:String,
    dof:String,
    gender:String,
    age:String,
    sig:String,
    salt:String,
    imagePath: String
): MutableLiveData<Response<PassportInsertApiClass>>{
    val addPassport = MutableLiveData<Response<PassportInsertApiClass>>()
    viewModelScope.launch {
        try {
            val result = repository.addPassport(userid, fistname, surname, nationality, dof, gender, age, sig, salt, imagePath)
            addPassport.postValue(result)
        } catch (e: Exception) {
            // 异常处理,比如文件不存在、网络错误等
            addPassport.postValue(null)
        }
    }
    return addPassport
}

5. Fragment层备份逻辑实现

用户触发备份操作时,先从SQLite查询所有待上传的护照数据,依次调用ViewModel方法上传即可,示例代码如下:

// 备份按钮点击事件
btnBackup.setOnClickListener {
    // 1. 先检查存储权限,Android 13+需要申请READ_MEDIA_IMAGES,低版本需要申请READ_EXTERNAL_STORAGE
    if (!checkStoragePermission()) {
        requestStoragePermission()
        return@setOnClickListener
    }

    lifecycleScope.launch {
        // 2. 从SQLite查询所有未上传的护照数据
        val pendingUploadList = sqliteDao.queryAllPendingUploadPassport()

        // 3. 遍历上传
        pendingUploadList.forEach { passport ->
            viewModel.addPassport(
                userid = passport.userId,
                fistname = passport.firstName,
                surname = passport.surName,
                nationality = passport.nationality,
                dof = passport.dof,
                gender = passport.gender,
                age = passport.age,
                sig = passport.sig,
                salt = passport.salt,
                imagePath = passport.imageLocalPath
            ).observe(viewLifecycleOwner) { response ->
                response?.let {
                    if (it.isSuccessful) {
                        // 上传成功,更新SQLite中该条数据的上传状态,避免重复上传
                        sqliteDao.updateUploadStatus(passport.id, UPLOAD_SUCCESS)
                    } else {
                        // 上传失败,可标记为待重试
                        sqliteDao.updateUploadStatus(passport.id, UPLOAD_FAILED)
                    }
                } ?: run {
                    // 异常提示
                    Toast.makeText(requireContext(), "上传失败:${passport.firstName}的护照信息", Toast.LENGTH_SHORT).show()
                }
            }
        }

        // 全部上传完成后提示用户
        Toast.makeText(requireContext(), "备份操作完成", Toast.LENGTH_SHORT).show()
    }
}

注意事项

  • 适配分区存储:Android 10及以上版本如果访问公共目录下的图片,不需要申请存储权限,直接通过Uri加载即可,如果是应用私有目录下的图片,全程不需要权限。
  • 图片压缩:上传前可先对图片进行压缩,避免上传大文件导致超时或流量消耗过高。
  • 批量上传优化:如果数据量较大,建议添加上传进度条,且支持断点续传或失败重试机制。
  • 服务端校验:服务端需要对上传的图片格式、大小进行校验,避免恶意文件上传。

内容的提问来源于stack exchange,提问作者androi useru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 16:36:03