feat: 增加媒体格式校验,支持 JPEG、PNG、GIF、MP4、WebM、WebP 格式

This commit is contained in:
km2023
2026-04-20 16:19:10 +08:00
parent 1dcae6e2f1
commit ed34c59c35
3 changed files with 51 additions and 2 deletions

View File

@@ -169,14 +169,20 @@ sdk.deleteFile(fileKey)
### 文件传输
```kotlin
// 传输媒体文件 (自动转换为 ANI 格式)
// 支持: mp4, jpg, png, ani
// 传输媒体文件 (格式校验 → ANI 转换 → prepare → transfer)
sdk.transferMedia(fileUrl)
// 监听进度
sdk.transferProgress.collect { percent -> /* 0-100 */ }
```
**支持的媒体格式:** JPEG、PNG、GIF、MP4、WebM、WebP
格式校验规则:
- 优先从 URL path 提取扩展名(自动忽略查询参数)
- 无扩展名时通过 HEAD 请求检查 `Content-Type`
- 不支持的格式抛出 `DuooomiBleError.UnsupportedFormat`
### 固件升级
```kotlin

View File

@@ -10,6 +10,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URI
import java.net.URL
class DuooomiBleSDK(context: Context, val config: DuooomiBleConfig) {
@@ -303,6 +304,46 @@ class DuooomiBleSDK(context: Context, val config: DuooomiBleConfig) {
// MARK: - High-Level APIs
companion object {
private val SUPPORTED_EXTENSIONS = setOf("jpeg", "jpg", "png", "gif", "mp4", "webm", "webp")
private val SUPPORTED_MIME_TYPES = setOf(
"image/jpeg", "image/png", "image/gif", "image/webp",
"video/mp4", "video/webm"
)
}
private fun extractExtension(urlString: String): String? {
return try {
val path = URI(urlString).path ?: return null
val dot = path.lastIndexOf('.')
if (dot >= 0 && dot < path.length - 1) path.substring(dot + 1).lowercase() else null
} catch (_: Exception) { null }
}
private suspend fun validateMediaFormat(url: String) {
val ext = extractExtension(url)
if (ext != null) {
if (SUPPORTED_EXTENSIONS.contains(ext)) return
throw DuooomiBleError.UnsupportedFormat(ext)
}
val mime = withContext(Dispatchers.IO) {
val conn = java.net.URL(url).openConnection() as HttpURLConnection
try {
conn.requestMethod = "HEAD"
conn.connectTimeout = 10_000
conn.readTimeout = 10_000
conn.contentType?.split(";")?.firstOrNull()?.trim()?.lowercase()
} finally {
conn.disconnect()
}
}
if (mime == null || !SUPPORTED_MIME_TYPES.contains(mime)) {
throw DuooomiBleError.UnsupportedFormat(mime ?: "unknown")
}
}
suspend fun transferMedia(fileUrl: String) {
ensureConnected()
val url = fileUrl.trim()
@@ -315,6 +356,7 @@ class DuooomiBleSDK(context: Context, val config: DuooomiBleConfig) {
aniUrl = url
BleLog.i("Direct ANI transfer: $url", "Transfer")
} else {
validateMediaFormat(url)
BleLog.i("Converting to ANI: $url", "Transfer")
try {
aniUrl = aniConverter.convert(url)

View File

@@ -24,6 +24,7 @@ sealed class DuooomiBleError(message: String) : Exception(message) {
class DeleteFileFailed(val status: Int) : DuooomiBleError("Delete failed: status=$status")
class PrepareTransferFailed(val status: String) : DuooomiBleError("Prepare failed: $status")
class TransferFailed(reason: String) : DuooomiBleError("Transfer failed: $reason")
class UnsupportedFormat(val format: String) : DuooomiBleError("Unsupported file format: $format. Supported: jpeg, png, gif, mp4, webm, webp")
object InvalidFrame : DuooomiBleError("Invalid protocol frame")
object PermissionDenied : DuooomiBleError("Bluetooth permission denied")
}