当前位置:首页 > 行业动态 > 正文

安卓上传图片到服务器端

Android上传图片至服务器需配置网络权限,通过FileProvider获取图片路径,压缩后使用Retrofit+MultipartBody构建POST请求,异步上传至后端接口,服务器解析并存储文件

准备工作

添加必要权限

AndroidManifest.xml中声明存储和网络权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

依赖库配置

build.gradle添加网络请求库(如Retrofit)和图片处理库:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

图片选择与获取

通过文件选择器获取图片

fun selectImage() {
    val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
        type = "image/"
        putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
    }
    startActivityForResult(intent, IMAGE_PICK_CODE)
}

处理返回结果

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == IMAGE_PICK_CODE && resultCode == RESULT_OK) {
        val uri = data?.data
        // 处理URI(需考虑Android 10+沙盒机制)
    }
}

图片预处理

图片压缩(可选)

使用Luban库进行压缩:

implementation 'top.zibin:luban:1.1.0'
Luban.with(this)
    .load(uri)
    .setCompressListener {
        // 获取压缩后的文件路径
    }.launch()

文件路径转换(Android 10+适配)

fun getRealPathFromUri(context: Context, uri: Uri): String? {
    return when (uri.scheme) {
        "content" -> {
            val projection = arrayOf(MediaStore.Images.Media.DATA)
            context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
                val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
                cursor.moveToFirst()
                cursor.getString(columnIndex)
            }
        }
        "file" -> uri.path
        else -> null
    }
}

上传实现

定义Retrofit接口

interface ApiService {
    @Multipart
    @POST("upload/image")
    suspend fun uploadImage(
        @Part file: MultipartBody.Part,
        @Part("description") description: RequestBody
    ): Response<UploadResponse>
}

创建Retrofit实例

val retrofit = Retrofit.Builder()
    .baseUrl("https://yourserver.com/api/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
val apiService = retrofit.create(ApiService::class.java)

构建请求体

fun prepareFilePart(filePath: String): MultipartBody.Part {
    val file = File(filePath)
    val requestBody = RequestBody.create(MediaType.parse("image/jpeg"), file)
    return MultipartBody.Part.createFormData("image", file.name, requestBody)
}

执行上传(协程方式)

GlobalScope.launch(Dispatchers.IO) {
    try {
        val response = apiService.uploadImage(
            filePart = prepareFilePart(imagePath),
            description = RequestBody.create(null, "Mobile Upload")
        )
        if (response.isSuccessful) {
            // 处理成功逻辑
        } else {
            // 处理错误响应
        }
    } catch (e: Exception) {
        // 网络错误处理
    }
}

进度监听实现

自定义OkHttp拦截器

class ProgressInterceptor(private val listener: (Long, Long, Boolean) -> Unit) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response = chain.proceed(chain.request())
        val contentLength = response.body?.contentLength() ?: -1
        val buffer = ByteArray(2048)
        var totalRead = 0L
        response.body?.source()?.let { source ->
            val inputStream = source.inputStream()
            while (true) {
                val read = inputStream.read(buffer).takeIf { it >= 0 } ?: break
                totalRead += read
                listener(totalRead, contentLength, totalRead == contentLength)
            }
        }
        return response
    }
}

集成进度监听

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(ProgressInterceptor { bytesRead, totalBytes, done ->
        runOnUiThread {
            progressBar.max = totalBytes.toInt() // 需处理-1情况
            progressBar.progress = bytesRead.toInt()
            if (done) showToast("上传完成")
        }
    })
    .build()
val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

服务器端接收示例(Node.js)

const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({ dest: 'uploads/' });
app.post('/upload/image', upload.single('image'), (req, res) => {
    const file = req.file;
    // 保存文件信息到数据库或进行其他处理
    res.json({ code: 200, path: file.path });
});

完整流程示意图

步骤 客户端操作 服务端操作 数据格式
1 选择本地图片 content:// URI
2 图片压缩处理 二进制文件流
3 构建Multipart请求 multipart/form-data
4 上传文件流 接收文件流 分块传输编码
5 进度监听 暂存文件 临时缓存
6 完成回调 持久化存储 JSON响应

常见问题与解决方案

Q1:Android 10+系统无法读取外部存储?

解决方案

  1. 使用MediaStore.Images.Media.insertImage()替代直接文件访问
  2. 申请MANAGE_EXTERNAL_STORAGE权限(需谨慎)
  3. 建议使用Content URI方式处理文件

Q2:大文件上传失败如何处理?

解决方案

  1. 启用HTTP分块上传(chunked encoding)
  2. 服务端配置最大接收内存限制
  3. 客户端实现断点续传机制
  4. 示例代码(Retrofit):
    val requestBody = RequestBody.create(MediaType.parse("image/jpeg"), file)
    val part = MultipartBody.Part.createFormData("image", file.name, requestBody)
0