文件、图片与录音
im.files 处理附件、头像和群文件。媒体文件 URL 是稳定的业务标识,实际下载需换取有时效的授权地址。服务端的用途和限制见媒体文件。
文件来源
import com.deeprespond.im.*
import java.io.File
val local = DRFile.fromFile(File(context.cacheDir, "report.pdf"), "report.pdf", "application/pdf")
val memory = DRFile.fromBytes("你好".toByteArray(), "note.txt", "text/plain")
val selected = DRFile.fromUri(context, uri)fromUri 是 Android 的扩展入口,星号导入包含它。文件选择器由 App 提供;需要跨重启保留系统 URI 时按系统规则取得权限。进入发送队列后 SDK 处理持久保存,但读取原始文件前 URI 必须有效。fromSource 面向平台适配,一般 App 使用上述三个入口。
随消息上传
im.messages.send(SendTarget.User("bob"), OutgoingContent.Image(file))
im.messages.send(SendTarget.User("bob"), OutgoingContent.File(file, name = "报告.pdf"))SDK 执行上传和发送,进度在本地消息状态中。独立上传返回任务:
import com.deeprespond.im.*
val task = im.files.upload(file, UploadPurpose.ATTACHMENT, FileKind.FILE, name = "报告.pdf")
val uploaded = task.await()
println(uploaded.url)UploadTask.progress 是 StateFlow,Progress.loaded / total 为字节,未知 total 为 -1。task.cancel 后 await 以 aborted 失败。协程取消等待和 task.cancel 不等价;业务真正取消任务时调用 cancel。
头像与群文件
USER_AVATAR 上传结果的 url 交给 users.updateMe;GROUP_AVATAR 交给 groups.update;CHATROOM_AVATAR 交给聊天室资料更新。群文件需要 groupId:
val task = im.files.upload(file, UploadPurpose.GROUP_FILE, FileKind.FILE, groupId = groupId)
val uploaded = task.await()
val page = im.files.groupFiles(groupId)
println(uploaded.fileId)
println(page.fileCount)groupFiles 返回 items、nextCursor、fileCount、usedBytes,deleteGroupFile 按权限删除。上传成功不等于已经作为消息发送。
下载与缓存
import com.deeprespond.im.*
val task = im.files.download(DownloadSource.File(fileUrl), conflict = Conflict.RENAME)
val saved = task.await()
println(saved.name)来源也可为 DownloadSource.Thumbnail(url)、DownloadSource.Message(message)。saveTo 指定目标目录(File),Conflict 可选 RENAME、OVERWRITE、FAIL。DownloadTask.cancel 会删除未完成的临时文件,await 抛 aborted。
resolveUrl 返回 downloadUrl、expiresAt;resolveUrls 批量结果逐项带 error。授权地址会过期,不应存成长期图片标识。localPath(url) 获取 SDK 按用户管理的图片缓存;clearImageCache() 清理。已经离开群或被内容安全屏蔽的文件可能无法再次下载。
图片与视频预处理
MediaOptions 默认开启图片压缩:最长边 2048、JPEG 质量 85、图片缓存 300 MiB。compressImages = false 只关闭压缩,默认去元数据仍执行。自定义 preprocess 在 IO 线程执行,负责自行清除不需要的元数据。
files.prepare(file, kind, original) 返回 PreparedFile:处理后的文件、kind、format、size、宽高、duration、thumbnail、downgraded、metadataKept。无法保持原媒体类型时可能降级为 FILE,按返回值展示。
PreparedFile 未交给后续上传或发送时,应清理不用的临时文件。DRFile.withKeepMetadata() 明确允许保留元数据,只在业务确实需要时使用。
Coil 图片接入
加入 im-coil,使用 Coil 3 并注册 SDK 组件:
import coil3.ImageLoader
import com.deeprespond.im.coil.*
val loader = ImageLoader.Builder(context)
.components { addDRImage(im) }
.build()
val model = DRImage(fileUrl)把 model 交给自己的 Coil ImageView / Compose 组件。DRImage.of(message, thumbnail = true) 从消息选择缩略图,结果可能为 null。缓存键包含当前用户,不能自行将有时效授权地址当作永久模型。
录音
App 在清单声明 RECORD_AUDIO,并在使用前取得麦克风权限:
import com.deeprespond.im.*
import kotlin.time.Duration.Companion.seconds
val recording = VoiceRecorder.start(context, maxDuration = 60.seconds)
// 用户点击停止时;不是开始后立即停止。
val voice = recording.stop()
im.messages.send(SendTarget.User("bob"), voice.toContent())Recording.level 为音量 Flow;ended 表示达到最长时长或被中断,reason 为 MAX_DURATION / INTERRUPTED。cancel 放弃并删除录音。已有录音、正在通话、权限未授予或设备错误时不能开始。切到后台或失去音频焦点可能中断录音,监听 ended 更新界面。输出为 AAC / m4a,不需要额外 FFmpeg。
完整文件接口见FilesModule,录音接口见Recording。
