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

如何在Android中使用HTTP PUT结合Retrofit上传图片、视频、PDF等任意文件?

Retrofit HTTP PUT File Upload for Android (Supports Large Files)

Hey there! I feel your pain—spending a week stuck on something that feels like it should be straightforward is the worst. Let's get you sorted with Retrofit PUT file uploads, even for big files like videos or PDFs.

Step 1: Confirm Your Dependencies

First, make sure you have Retrofit and OkHttp set up in your module-level build.gradle:

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.okhttp3:okhttp:4.10.0'
}

(You can swap in the latest stable versions if you prefer)

Step 2: Define Your Retrofit PUT Interface

The only major difference from POST uploads is swapping @POST for @PUT. For raw file uploads (where your backend expects the file content directly in the request body), use this interface:

import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.PUT
import retrofit2.http.Path
import okhttp3.RequestBody
import okhttp3.ResponseBody

interface FileUploadApi {
    // Replace with your actual backend PUT endpoint path
    @PUT("upload/{file-id}")
    suspend fun uploadFile(
        @Path("file-id") fileId: String, // Optional: if your endpoint needs a unique file identifier
        @Body fileRequestBody: RequestBody
    ): Response<ResponseBody>
}

Step 3: Handle Large Files & Track Upload Progress

Large files can cause OutOfMemory errors if loaded entirely into memory. Use a custom RequestBody to stream the file in chunks and track progress (a huge win for user experience):

import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.internal.Util
import okio.BufferedSink
import okio.source
import java.io.File

class ProgressRequestBody(
    private val file: File,
    private val mediaType: MediaType?,
    private val onProgressUpdate: (Int) -> Unit
) : RequestBody() {

    override fun contentType(): MediaType? = mediaType

    override fun contentLength(): Long = file.length()

    override fun writeTo(sink: BufferedSink) {
        val source = file.source()
        val buffer = ByteArray(Util.DEFAULT_BUFFER_SIZE)
        var bytesWritten = 0L

        source.use { input ->
            while (true) {
                val read = input.read(buffer)
                if (read == -1) break
                bytesWritten += read
                sink.write(buffer, 0, read)
                // Calculate and send progress update to the UI
                val progress = ((bytesWritten * 100) / file.length()).toInt()
                onProgressUpdate(progress)
            }
        }
    }
}

Step 4: Implement the Upload Call

Now tie everything together. Use Kotlin Coroutines to run the network call on a background thread (standard practice for Android to avoid ANRs):

import android.os.Bundle
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType
import retrofit2.Retrofit

class UploadActivity : AppCompatActivity() {
    private lateinit var progressBar: ProgressBar

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_upload)
        progressBar = findViewById(R.id.progress_bar)

        // Replace with your actual file path (e.g., from a file picker result)
        val targetFile = File(getExternalFilesDir(null), "large_presentation.pdf")
        // Adjust the media type based on your file: image/jpeg, video/mp4, application/pdf, etc.
        val mediaType = MediaType.parse("application/pdf")

        // Create Retrofit instance with a longer timeout for large files
        val retrofit = Retrofit.Builder()
            .baseUrl("https://your-backend-base-url.com/")
            .client(okhttp3.OkHttpClient.Builder()
                .connectTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
                .writeTimeout(120, java.util.concurrent.TimeUnit.SECONDS)
                .build())
            .build()

        val uploadApi = retrofit.create(FileUploadApi::class.java)

        // Launch the upload process
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val progressBody = ProgressRequestBody(targetFile, mediaType) { progress ->
                    // Update progress bar on the main thread
                    withContext(Dispatchers.Main) {
                        progressBar.progress = progress
                    }
                }

                val response = uploadApi.uploadFile("my-unique-file-id", progressBody)

                withContext(Dispatchers.Main) {
                    if (response.isSuccessful) {
                        Toast.makeText(this@UploadActivity, "Upload successful!", Toast.LENGTH_SHORT).show()
                    } else {
                        Toast.makeText(this@UploadActivity, "Upload failed: ${response.message()}", Toast.LENGTH_SHORT).show()
                    }
                }
            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    Toast.makeText(this@UploadActivity, "Error: ${e.localizedMessage}", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

Bonus: Multipart PUT Uploads (If Your Backend Expects Form Data)

If your backend needs form data (like a file field plus additional parameters), @Multipart works with PUT too:

interface FileUploadApi {
    @Multipart
    @PUT("upload")
    suspend fun uploadMultipartFile(
        @Part file: okhttp3.MultipartBody.Part,
        @Part("file_name") fileName: RequestBody
    ): Response<ResponseBody>
}

Create the multipart parts like this:

val filePart = okhttp3.MultipartBody.Part.createFormData(
    "file",
    targetFile.name,
    targetFile.asRequestBody(mediaType)
)
val fileNameBody = RequestBody.create(MediaType.parse("text/plain"), targetFile.name)

Key Notes to Avoid Headaches

  • Permissions: Add <uses-permission android:name="android.permission.INTERNET"/> to your AndroidManifest.xml. For local file access, you'll need storage permissions (adjust for Android 13+ scoped storage rules).
  • Timeout Settings: Large files require longer write timeouts—tweak the OkHttpClient settings as shown.
  • Threading: Never run network calls on the main thread to avoid app crashes or ANRs.

This setup streams files instead of loading them fully into memory, so it works seamlessly with any file size. Let me know if you hit any snags!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 23:39:08