This commit is contained in:
lunaticbum 2025-08-18 18:44:11 +09:00
parent b34750099e
commit f7f71ca195
2 changed files with 69 additions and 30 deletions

View File

@ -493,7 +493,7 @@ class GeckoWeb : BWebview {
override fun onExternalResponse(session: GeckoSession, response: WebResponse) { override fun onExternalResponse(session: GeckoSession, response: WebResponse) {
Blog.LOGE("response >>> ${response.uri} ") Blog.LOGE("response >>> ${response.uri} ")
if (response.uri.contains(".apk")) return if (response.uri.contains(".apk")) return
val url = response.uri val url = response.uri
val filename = "${url.substringAfterLast("/")}_${System.currentTimeMillis()}.mp4" // 파일명 추출, 없으면 URL에서 가져옴 val filename = "${url.substringAfterLast("/")}_${System.currentTimeMillis()}.mp4" // 파일명 추출, 없으면 URL에서 가져옴

View File

@ -12,6 +12,8 @@ import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import java.io.File import java.io.File
import java.net.URLConnection
import java.util.regex.Pattern
object CommonUtils { object CommonUtils {
fun dpToPx(context: Context, dp: Float): Int { fun dpToPx(context: Context, dp: Float): Int {
@ -27,48 +29,90 @@ object CommonUtils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
} }
fun getFileTypeBySignature(file: File): String? {
val buffer = ByteArray(12) // 동영상 포맷 확인용으로 충분히 크게 버퍼 확장
file.inputStream().use { it.read(buffer, 0, buffer.size) }
// GIF
if (buffer.sliceArray(0..5).contentEquals("GIF87a".toByteArray()) || fun getFileTypeBySignature(file: File): String? {
buffer.sliceArray(0..5).contentEquals("GIF89a".toByteArray())) { val buffer = ByteArray(12)
return "gif" val readBytes = file.inputStream().use { it.read(buffer, 0, buffer.size) }
// 1. 시그니처 검사 (기존 방식)
if (readBytes >= 6) {
val gifSignature1 = "GIF87a".toByteArray(Charsets.US_ASCII)
val gifSignature2 = "GIF89a".toByteArray(Charsets.US_ASCII)
if (buffer.copyOfRange(0, 6).contentEquals(gifSignature1)
|| buffer.copyOfRange(0, 6).contentEquals(gifSignature2)
) {
return "gif"
}
} }
// JPEG
if (buffer[0] == 0xFF.toByte() && buffer[1] == 0xD8.toByte() && buffer[2] == 0xFF.toByte()) { if (readBytes >= 3 &&
buffer[0] == 0xFF.toByte() && buffer[1] == 0xD8.toByte() && buffer[2] == 0xFF.toByte()
) {
return "jpg" return "jpg"
} }
// PNG
if (buffer.sliceArray(0..7).contentEquals(byteArrayOf(0x89.toByte(),'P'.toByte(),'N'.toByte(),'G'.toByte(),0x0D,0x0A,0x1A,0x0A))) { if (readBytes >= 8 &&
buffer.copyOfRange(0, 8).contentEquals(
byteArrayOf(
0x89.toByte(), 'P'.toByte(), 'N'.toByte(), 'G'.toByte(),
0x0D, 0x0A, 0x1A, 0x0A
)
)
) {
return "png" return "png"
} }
// MP4 (ftyp... 브랜드 체크, 보통 4~8바이트 확인) if (readBytes >= 12 &&
// "ftyp"는 4바이트 offset 4 ~ 7에 위치 buffer.copyOfRange(4, 8).contentEquals("ftyp".toByteArray(Charsets.US_ASCII))
if (buffer.size >= 12 && buffer.sliceArray(4..7).contentEquals("ftyp".toByteArray())) { ) {
return "mp4" return "mp4"
} }
// AVI (큰 헤더 "RIFF....AVI ") if (readBytes >= 12 &&
if (buffer.size >= 12 && buffer.copyOfRange(0, 4).contentEquals("RIFF".toByteArray(Charsets.US_ASCII)) &&
buffer.sliceArray(0..3).contentEquals("RIFF".toByteArray()) && buffer.copyOfRange(8, 12).contentEquals("AVI ".toByteArray(Charsets.US_ASCII))
buffer.sliceArray(8..11).contentEquals("AVI ".toByteArray())) { ) {
return "avi" return "avi"
} }
// MKV (Matroska) - EBML 헤더: 0x1A 0x45 0xDF 0xA3 if (readBytes >= 4 &&
if (buffer.size >= 4 && buffer[0] == 0x1A.toByte() &&
buffer[0] == 0x1A.toByte() && buffer[1] == 0x45.toByte() && buffer[1] == 0x45.toByte() &&
buffer[2] == 0xDF.toByte() && buffer[3] == 0xA3.toByte()) { buffer[2] == 0xDF.toByte() &&
buffer[3] == 0xA3.toByte()
) {
return "mkv" return "mkv"
} }
// 2. MIME 타입 검사 (Optional, 정확도 향상)
val mimeType = URLConnection.guessContentTypeFromStream(file.inputStream())
if (mimeType != null) {
when {
mimeType.equals("image/gif", ignoreCase = true) -> return "gif"
mimeType.equals("image/jpeg", ignoreCase = true) -> return "jpg"
mimeType.equals("image/png", ignoreCase = true) -> return "png"
mimeType.startsWith("video/mp4", ignoreCase = true) -> return "mp4"
mimeType.startsWith("video/x-msvideo", ignoreCase = true) -> return "avi"
mimeType.startsWith("video/x-matroska", ignoreCase = true) -> return "mkv"
}
}
// 3. 정규식 기반 시그니처 검사 (GIF 전용 예)
if (readBytes >= 6) {
val sigStr = String(buffer, 0, 6, Charsets.US_ASCII)
val gifPattern = Pattern.compile("GIF8[79]a")
if (gifPattern.matcher(sigStr).matches()) {
return "gif"
}
}
return null return null
} }
fun downloadFileWithOkHttp(context: Context,refferer : Uri, fileUrl: String) { fun downloadFileWithOkHttp(context: Context,refferer : Uri, fileUrl: String) {
android.app.AlertDialog.Builder(context) android.app.AlertDialog.Builder(context)
.setTitle("파일 다운로드") .setTitle("파일 다운로드")
@ -95,15 +139,12 @@ object CommonUtils {
).show() ).show()
} }
} else { } else {
// Content-Type에서 확장자 추출 // Content-Type에서 확장자 추출
val contentType = response.header("Content-Type") val contentType = response.header("Content-Type")
Blog.LOGE("downloadFileWithOkHttp contentType $contentType") Blog.LOGE("downloadFileWithOkHttp contentType $contentType")
var extension = contentType?.let { var extension = contentType?.let {
MimeTypeMap.getSingleton().getExtensionFromMimeType(it) MimeTypeMap.getSingleton().getExtensionFromMimeType(it)
} }
// 확장자 없으면 URL에서 추출하거나 기본값 "dat" 사용 // 확장자 없으면 URL에서 추출하거나 기본값 "dat" 사용
if (extension.isNullOrBlank()) { if (extension.isNullOrBlank()) {
extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl) extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl)
@ -112,9 +153,6 @@ object CommonUtils {
} }
} }
Blog.LOGE("downloadFileWithOkHttp extension $extension") Blog.LOGE("downloadFileWithOkHttp extension $extension")
// if (extension.equals("bin")){
//// extension = "jpg"
// }
val fileName = val fileName =
"${ "${
refferer.host?.replace( refferer.host?.replace(
@ -133,6 +171,7 @@ object CommonUtils {
val resultFile = if (extension in listOf("bin", "dat")) { val resultFile = if (extension in listOf("bin", "dat")) {
val realExt = getFileTypeBySignature(file) val realExt = getFileTypeBySignature(file)
Blog.LOGE("downloadFileWithOkHttp extension $realExt")
when { when {
realExt.isNullOrBlank() -> file realExt.isNullOrBlank() -> file
realExt == extension -> file realExt == extension -> file
@ -142,7 +181,7 @@ object CommonUtils {
",", ",",
"_" "_"
) )
}_${System.currentTimeMillis()}.$extension") }_${System.currentTimeMillis()}.$realExt")
if (file.renameTo(newFile)) newFile else file if (file.renameTo(newFile)) newFile else file
} }
} }