From ed34c59c35ef79491dd410076956cae2b19ff2e9 Mon Sep 17 00:00:00 2001 From: km2023 Date: Mon, 20 Apr 2026 16:19:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=AA=92=E4=BD=93?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E6=A0=A1=E9=AA=8C=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20JPEG=E3=80=81PNG=E3=80=81GIF=E3=80=81MP4=E3=80=81WebM?= =?UTF-8?q?=E3=80=81WebP=20=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++- .../kotlin/com/duooomi/ble/DuooomiBleSDK.kt | 42 +++++++++++++++++++ .../kotlin/com/duooomi/ble/core/BleTypes.kt | 1 + 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8304fae..3815747 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt index e7a9c51..0051305 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/DuooomiBleSDK.kt @@ -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) diff --git a/sdk/src/main/kotlin/com/duooomi/ble/core/BleTypes.kt b/sdk/src/main/kotlin/com/duooomi/ble/core/BleTypes.kt index 8eecfd1..a757498 100644 --- a/sdk/src/main/kotlin/com/duooomi/ble/core/BleTypes.kt +++ b/sdk/src/main/kotlin/com/duooomi/ble/core/BleTypes.kt @@ -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") }