如何在Android Jetpack Compose中仅选图库视频及推荐上传方案?
一、Compose中仅选择视频的实现
在Compose中选择视频依然依赖Android的Intent体系,但需要配合Activity Result API和Compose的rememberLauncherForActivityResult来处理结果,替代传统的startActivityForResult写法。
实现步骤:
定义视频选择器Launcher
使用ActivityResultContracts.GetContent并指定MIME类型为video/*,限制仅能选择视频文件:val videoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent() ) { uri: Uri? -> uri?.let { // 处理选中的视频Uri,比如暂存Uri用于后续上传流程 } }添加触发选择的UI按钮
在Compose布局中添加交互按钮,点击时启动视频选择器:Button(onClick = { videoPickerLauncher.launch("video/*") }) { Text("选择视频") }可选:将Uri转换为本地文件
如果需要将Uri转为可直接操作的File对象,可通过ContentResolver实现:fun getVideoFileFromUri(context: Context, uri: Uri): File? { val contentResolver = context.contentResolver val inputStream = contentResolver.openInputStream(uri) ?: return null val tempFile = File(context.cacheDir, "temp_video_${System.currentTimeMillis()}.mp4") inputStream.use { input -> tempFile.outputStream().use { output -> input.copyTo(output) } } return tempFile }
二、视频上传方案推荐
1. Retrofit + OkHttp Multipart(首选,原生稳定)
你计划的Retrofit Multipart方案完全可行,这是Android文件上传的标准方案——Retrofit底层基于OkHttp,天然复用OkHttp的超时配置、拦截器等特性,无需额外依赖。
实现示例:
定义Retrofit上传接口:
interface VideoUploadApi { @Multipart @POST("upload/video") suspend fun uploadVideo( @Part video: MultipartBody.Part, @Part("title") title: RequestBody // 可选的额外参数 ): Response<UploadResult> // UploadResult为自定义的响应实体类 }构建Multipart请求并执行上传:
fun prepareVideoPart(file: File): MultipartBody.Part { val requestBody = file.asRequestBody("video/mp4".toMediaTypeOrNull()) return MultipartBody.Part.createFormData( name = "video_file", // 对应后端接收的参数名 filename = file.name, body = requestBody ) } // 在ViewModel或Repository中调用上传逻辑 suspend fun uploadVideo(file: File, title: String) { val videoPart = prepareVideoPart(file) val titleBody = title.toRequestBody("text/plain".toMediaTypeOrNull()) val response = videoUploadApi.uploadVideo(videoPart, titleBody) if (response.isSuccessful) { // 上传成功后的处理逻辑 } else { // 上传失败的错误处理 } }
2. 大视频上传优化(可选)
如果上传的视频文件较大(如100MB以上),可以添加以下优化:
上传进度监听:自定义OkHttp的
RequestBody来实时获取上传进度:class ProgressRequestBody( private val delegate: RequestBody, private val onProgress: (Int) -> Unit ) : RequestBody() { override fun contentType(): MediaType? = delegate.contentType() override fun writeTo(sink: BufferedSink) { val bufferedSink = object : ForwardingSink(sink) { var bytesWritten = 0L var contentLength = 0L override fun write(source: Buffer, byteCount: Long) { super.write(source, byteCount) if (contentLength == 0L) contentLength = contentLength() bytesWritten += byteCount onProgress(((bytesWritten * 100) / contentLength).toInt()) } } delegate.writeTo(BufferedSink(bufferedSink)) } }使用时将
file.asRequestBody()替换为ProgressRequestBody(...)即可获取实时进度。断点续传:需后端配合支持范围请求(Range Header),记录已上传字节数,下次上传时从断点处继续,避免重复上传已完成部分。
3. 其他可选方案
如果需要后台稳定上传,可结合AndroidX的WorkManager封装上传任务,避免页面销毁导致上传中断,但核心的文件上传逻辑依然依赖Retrofit/OkHttp的Multipart机制。
内容的提问来源于stack exchange,提问作者Ahmed Rabee

