This commit is contained in:
Evan You
2025-04-08 09:24:15 +08:00
parent 39887866ff
commit 50b5e4e422
18 changed files with 156 additions and 155 deletions

View File

@@ -15,8 +15,8 @@ android {
applicationId = "com.donut.mixfile"
minSdk = 26
targetSdk = 35
versionCode = 103
versionName = "1.13.3"
versionCode = 104
versionName = "1.13.4"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@@ -7,7 +7,7 @@
<meta content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"
name="viewport"/>
<title>MixFile</title>
<script type="module" crossorigin src="/assets/index-W8wx7JSe.js"></script>
<script type="module" crossorigin src="/assets/index-DrT-F2ns.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Ub7aM_kp.css">
</head>
<body>

View File

@@ -1,15 +0,0 @@
package com.donut.mixfile.server
import com.donut.mixfile.server.core.utils.bean.MixShareInfo
import com.donut.mixfile.ui.routes.home.getLocalServerAddress
import com.donut.mixfile.ui.routes.home.serverAddress
import com.donut.mixfile.util.getFileAccessUrl
val MixShareInfo.downloadUrl: String
get() = getFileAccessUrl(getLocalServerAddress(), this.toString(), fileName)
val MixShareInfo.lanUrl: String
get() = getFileAccessUrl(serverAddress, this.toString(), fileName)

View File

@@ -44,7 +44,7 @@ fun getCipher(mode: Int, key: ByteArray, iv: ByteArray): Cipher {
return cipher
}
suspend fun decryptAES(data: ByteReadChannel, key: ByteArray): ByteArray? {
suspend fun decryptAES(data: ByteReadChannel, key: ByteArray): ByteArray {
val iv = data.readRemaining(12).readByteArray()
val cipher = getCipher(Cipher.DECRYPT_MODE, key, iv)
val result = ByteArrayOutputStream()

View File

@@ -28,8 +28,9 @@ import kotlin.io.encoding.ExperimentalEncodingApi
@OptIn(ExperimentalEncodingApi::class)
fun MixFileServer.getDownloadRoute(): RoutingHandler {
return route@{
val shareInfoData = call.request.queryParameters["s"]
val referer = call.request.queryParameters["referer"]
val shareInfoData = call.parameters["s"]
val referer = call.parameters["referer"]
var name = call.parameters["name"]
if (shareInfoData == null) {
call.respondText("分享信息为空", status = HttpStatusCode.InternalServerError)
return@route
@@ -47,12 +48,15 @@ fun MixFileServer.getDownloadRoute(): RoutingHandler {
)
return@route
}
if (name.isNullOrEmpty()) {
name = shareInfo.fileName
}
var contentLength = shareInfo.fileSize
val range: LongRange? = call.request.ranges()?.mergeToSingle(contentLength)
call.response.apply {
header(
"Content-Disposition",
"inline; filename=\"${shareInfo.fileName.encodeURL()}\""
"inline; filename=\"${name.encodeURL()}\""
)
}
var fileList = mixFile.fileList.map { it to 0 }
@@ -85,25 +89,17 @@ private suspend fun MixFileServer.responseDownloadFileStream(
val sortedTask = SortedTask(downloadTaskCount)
val tasks = mutableListOf<Deferred<Unit>>()
while (!isClosedForWrite && fileList.isNotEmpty()) {
val currentMeta = fileList.removeAt(0)
val currentFile = fileList.removeAt(0)
val taskOrder = -fileList.size
sortedTask.prepareTask(taskOrder)
tasks.add(async {
val url = currentMeta.first
val (url, range) = currentFile
val dataBytes =
shareInfo.fetchFile(url, httpClient, referer ?: shareInfo.referer)
val range = currentMeta.second
if (dataBytes == null) {
call.respondText(
"下载失败",
status = HttpStatusCode.InternalServerError
)
return@async
}
sortedTask.addTask(taskOrder) {
val dataToWrite = when {
range == 0 -> dataBytes
range < 0 -> dataBytes.copyOfRange(0, -range)
range < 0 -> dataBytes.copyOfRange(0, -range) //一般无 < 0 的情况
else -> dataBytes.copyOfRange(range, dataBytes.size)
}
try {

View File

@@ -16,7 +16,8 @@ import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.put
import io.ktor.server.routing.route
import io.ktor.utils.io.jvm.javaio.toOutputStream
import io.ktor.utils.io.copyTo
import io.ktor.utils.io.jvm.javaio.toByteReadChannel
fun MixFileServer.getRoutes(): Routing.() -> Unit {
@@ -29,12 +30,12 @@ fun MixFileServer.getRoutes(): Routing.() -> Unit {
call.respondBytesWriter(
contentType = ContentType.parse(file.parseFileMimeType())
) {
fileStream.copyTo(this.toOutputStream())
fileStream.toByteReadChannel().copyTo(this)
}
}
route("/api") {
get("/download", getDownloadRoute())
put("/upload", getUploadRoute())
get("/download/{name?}", getDownloadRoute())
put("/upload/{name?}", getUploadRoute())
get("/upload_history") {
if (call.request.header("origin").isNotNull()) {
return@get call.respondText("此接口禁止跨域", status = HttpStatusCode.Forbidden)
@@ -42,7 +43,7 @@ fun MixFileServer.getRoutes(): Routing.() -> Unit {
call.respond(getFileHistory())
}
get("/file_info") {
val shareInfoStr = call.request.queryParameters["s"]
val shareInfoStr = call.parameters["s"]
if (shareInfoStr == null) {
call.respondText("分享信息为空", status = HttpStatusCode.InternalServerError)
return@get

View File

@@ -29,8 +29,8 @@ import kotlin.math.ceil
fun MixFileServer.getUploadRoute(): RoutingHandler {
return route@{
val key = generateRandomByteArray(32)
val name = call.request.queryParameters["name"]
val add = call.request.queryParameters["add"] ?: "true"
val name = call.parameters["name"]
val add = call.parameters["add"] ?: "true"
if (name.isNullOrEmpty()) {
call.respondText("需要文件名称", status = HttpStatusCode.InternalServerError)
return@route

View File

@@ -56,7 +56,7 @@ data class MixShareInfo(
private fun enc(input: String): String {
val bytes = input.encodeToByteArray()
val result = encryptAES(bytes, "123".hashMD5(), iv = "123".hashMD5().copyOf(12))
val result = encryptAES(bytes, "123".hashMD5())
return ENCODER.encode(result)
}
@@ -81,10 +81,10 @@ data class MixShareInfo(
url: String,
client: HttpClient,
referer: String = this.referer,
): ByteArray? {
): ByteArray {
val transformedUrl = Uploader.transformUrl(url)
val transformedReferer = Uploader.transformReferer(url, referer)
val result: ByteArray? = client.config {
val result: ByteArray = client.config {
install(HttpRequestRetry) {
maxRetries = 3
retryOnException(retryOnTimeout = true)
@@ -102,13 +102,11 @@ data class MixShareInfo(
channel.discard(headSize.toLong())
decryptAES(channel, ENCODER.decode(key))
}
if (result != null) {
val hash = url.split("#").getOrNull(1)
if (hash != null) {
val currentHash = result.hashMixSHA256()
if (!currentHash.contentEquals(hash)) {
throw Exception("文件遭到篡改")
}
val hash = url.split("#").getOrNull(1)
if (hash != null) {
val currentHash = result.hashMixSHA256()
if (!currentHash.contentEquals(hash)) {
throw Exception("文件遭到篡改")
}
}
return result
@@ -128,7 +126,7 @@ data class MixShareInfo(
fun contentType(): String = fileName.parseFileMimeType()
suspend fun fetchMixFile(client: HttpClient): MixFile? {
val decryptedBytes = fetchFile(url, client = client) ?: return null
val decryptedBytes = fetchFile(url, client = client)
return MixFile.fromBytes(decryptedBytes)
}

View File

@@ -10,20 +10,23 @@ import androidx.compose.ui.Modifier
import com.donut.mixfile.ui.component.common.MixDialogBuilder
import com.donut.mixfile.ui.component.common.SingleSelectItemList
import com.donut.mixfile.util.UseEffect
import com.donut.mixfile.util.compareByName
import com.donut.mixfile.util.file.favCategories
import com.donut.mixfile.util.file.favorites
import com.donut.mixfile.util.file.loadFileList
import com.donut.mixfile.util.file.showFileList
import com.donut.mixfile.util.objects.ProgressContent
import com.donut.mixfile.util.showToast
import com.donut.mixfile.util.sortByName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
fun openCategorySelect(default: String = "", onSelect: (String) -> Unit) {
MixDialogBuilder("收藏分类").apply {
setContent {
SingleSelectItemList(favCategories.toList().sortByName(), default) {
SingleSelectItemList(
favCategories.toList().sortedWith { str1, str2 -> str1.compareByName(str2) },
default
) {
onSelect(it)
closeDialog()
}

View File

@@ -34,7 +34,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
import com.donut.mixfile.ui.component.common.MixDialogBuilder
import com.donut.mixfile.ui.component.common.SingleSelectItemList
import com.donut.mixfile.ui.nav.MixNavPage
@@ -45,6 +44,7 @@ import com.donut.mixfile.ui.theme.colorScheme
import com.donut.mixfile.util.OnDispose
import com.donut.mixfile.util.cachedMutableOf
import com.donut.mixfile.util.catchError
import com.donut.mixfile.util.compareByName
import com.donut.mixfile.util.file.FileCardList
import com.donut.mixfile.util.file.FileDataLog
import com.donut.mixfile.util.file.downloadFile
@@ -53,7 +53,6 @@ import com.donut.mixfile.util.file.selectAndUploadFile
import com.donut.mixfile.util.file.showExportFileListDialog
import com.donut.mixfile.util.file.showFileInfoDialog
import com.donut.mixfile.util.formatFileSize
import com.donut.mixfile.util.parseSortNum
import com.donut.mixfile.util.showConfirmDialog
import com.donut.mixfile.util.showToast
import kotlinx.coroutines.Dispatchers
@@ -150,11 +149,11 @@ val Favorites = MixNavPage(
sortJob?.cancel()
sortJob = scope.launch(Dispatchers.IO) {
catchError {
val sorted = result.sortedBy {
val sorted = result.sortedWith { file1, file2 ->
if (!isActive) {
throw Exception("canceled")
throw Exception("排序取消")
}
it.name.parseSortNum()
file1.name.compareByName(file2.name)
}
withContext(Dispatchers.Main) {
if (resultCache == result) {
@@ -262,10 +261,7 @@ val Favorites = MixNavPage(
},
Pair("全部下载") {
selected.forEach {
val shareInfo = resolveMixShareInfo(it.shareInfoData)
if (shareInfo != null) {
downloadFile(shareInfo)
}
downloadFile(it)
}
showDownloadTaskWindow()
selected = emptySet()

View File

@@ -100,7 +100,7 @@ fun MainTheme(
window.statusBarColor = colorScheme.surface.toArgb()
window.navigationBarColor = colorScheme.surface.toArgb()
WindowCompat.getInsetsController(window, view).apply {
isAppearanceLightNavigationBars = true
isAppearanceLightStatusBars = true
isAppearanceLightNavigationBars = false
}
}

View File

@@ -12,17 +12,18 @@ import com.donut.mixfile.app
import com.donut.mixfile.appScope
import com.donut.mixfile.server.core.utils.genRandomString
import com.donut.mixfile.server.core.utils.ignoreError
import com.donut.mixfile.server.core.utils.isFalse
import com.donut.mixfile.server.mixFileServer
import com.donut.mixfile.ui.routes.home.getLocalServerAddress
import io.ktor.http.URLBuilder
import io.ktor.http.path
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.EOFException
import java.math.BigInteger
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.URL
import java.net.URLEncoder
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@@ -145,22 +146,44 @@ fun <T> List<T>.at(index: Long): T {
}
fun String.parseSortNum(): BigInteger {
val digits = StringBuilder()
for (char in this) {
if (char.isDigit()) {
digits.append(char)
}
}
return if (digits.isEmpty()) {
BigInteger.ZERO
} else {
BigInteger(digits.toString())
}
val numberStr = this.filter { it.isDigit() }.ifEmpty { "0" }
return BigInteger(numberStr)
}
fun Iterable<String>.sortByName(): List<String> {
return sortedBy { it.parseSortNum() }
fun extractNumber(str: String, start: Int): Long {
var result = 0L
var i = start
while (i < str.length && str[i].isDigit()) {
result = result * 10 + (str[i] - '0')
i++
}
return result
}
fun String.compareByName(str2: String): Int {
var i1 = 0
var i2 = 0
val str1 = this
while (i1 < str1.length && i2 < str2.length) {
// 处理数字部分
val char1 = str1[i1]
val char2 = str2[i2]
if (char1.isDigit() && char2.isDigit()) {
val num1 =
extractNumber(str1, i1).also { i1 += it.toString().length }
val num2 =
extractNumber(str2, i2).also { i2 += it.toString().length }
//相等则继续提取下个数字进行比较
if (num1 != num2) return num1.compareTo(num2)
}
// 处理非数字部分
else {
if (char1 != char2) return char1.compareTo(char2)
i1++
i2++
}
}
return str1.length.compareTo(str2.length)
}
fun <T> List<T>.at(index: Int): T {
@@ -227,15 +250,6 @@ fun genRandomHexString(length: Int = 32) = genRandomString(length, ('0'..'9') +
fun readRawFile(id: Int) = app.resources.openRawResource(id).readBytes()
fun isValidUri(uriString: String): Boolean {
try {
val uri = Uri.parse(uriString)
return uri != null && uri.scheme != null
} catch (e: Exception) {
return false
}
}
fun showError(e: Throwable, tag: String = "") {
Log.e(
"error",
@@ -248,26 +262,28 @@ fun getFileAccessUrl(
shareInfo: String,
fileName: String
): String {
return "${host}/api/download?s=${
URLEncoder.encode(
shareInfo,
"UTF-8"
)
}&accessKey=${mixFileServer.accessKey}#${fileName}"
return URLBuilder(host).apply {
path("api", "download", fileName)
fragment = fileName
parameters.apply {
append("s", shareInfo)
if (mixFileServer.enableAccessKey) {
append("accessKey", mixFileServer.accessKey)
}
}
}.buildString()
}
fun getIpAddressInLocalNetwork(): String {
val networkInterfaces = NetworkInterface.getNetworkInterfaces().iterator().asSequence()
val localAddresses = networkInterfaces.flatMap {
it.inetAddresses.asSequence()
.filter { inetAddress ->
inetAddress.isSiteLocalAddress && inetAddress?.hostAddress?.contains(":")
.isFalse() &&
inetAddress.hostAddress != "127.0.0.1"
}
.map { inetAddress -> inetAddress.hostAddress }
}
return localAddresses.firstOrNull() ?: "127.0.0.1"
return NetworkInterface.getNetworkInterfaces()?.asSequence()
?.filter { it.isUp }
?.flatMap { it.inetAddresses.asSequence() }
?.find { addr ->
addr is Inet4Address &&
addr.isSiteLocalAddress &&
addr.hostAddress != "127.0.0.1"
}?.hostAddress ?: "127.0.0.1"
}
inline fun errorDialog(title: String, block: () -> Unit) {

View File

@@ -41,6 +41,7 @@ import com.donut.mixfile.server.core.utils.isNotNull
import com.donut.mixfile.ui.component.common.MixDialogBuilder
import com.donut.mixfile.ui.theme.MainTheme
import com.donut.mixfile.ui.theme.colorScheme
import com.donut.mixfile.util.objects.MixActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -80,6 +81,9 @@ fun addComposeView(
scheme: ColorScheme? = null,
content: @Composable (removeView: () -> Unit) -> Unit
): () -> Unit {
if (MixActivity.firstActiveActivity() == null) {
return {}
}
return addContentView(
ComposeView(currentActivity).apply {
setContent {

View File

@@ -34,8 +34,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.donut.mixfile.server.core.utils.parseFileMimeType
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
import com.donut.mixfile.server.downloadUrl
import com.donut.mixfile.server.serverStarted
import com.donut.mixfile.ui.routes.home.tryResolveFile
import com.donut.mixfile.ui.theme.colorScheme
@@ -85,18 +83,15 @@ fun PreviewCard(
verticalArrangement = Arrangement.Bottom
) {
if (serverStarted) {
val shareInfo = resolveMixShareInfo(fileDataLog.shareInfoData)
if (shareInfo != null) {
if (isImage && fileDataLog.size < 1024 * 1024 * 20 || isVideo) {
ImageContent(
shareInfo.downloadUrl,
Modifier
.height(200.dp)
.fillMaxSize(),
scale = ContentScale.Crop
if (isImage && fileDataLog.size < 1024 * 1024 * 20 || isVideo) {
ImageContent(
fileDataLog.downloadUrl,
Modifier
.height(200.dp)
.fillMaxSize(),
scale = ContentScale.Crop
)
}
)
}
}
Column(
@@ -135,7 +130,7 @@ fun FileCardList(
cardList: List<FileDataLog>,
selected: Set<FileDataLog> = setOf(),
onClick: (FileDataLog) -> Unit = {
tryResolveFile(it.shareInfoData)
showFileInfoDialog(it)
},
longClick: (FileDataLog) -> Unit = {},
) {

View File

@@ -7,7 +7,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.alibaba.fastjson2.annotation.JSONField
import com.donut.mixfile.server.core.utils.bean.MixShareInfo
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
@@ -15,6 +14,7 @@ import com.donut.mixfile.ui.component.common.MixDialogBuilder
import com.donut.mixfile.ui.routes.autoAddFavorite
import com.donut.mixfile.ui.routes.favorites.currentCategory
import com.donut.mixfile.ui.routes.home.getLocalServerAddress
import com.donut.mixfile.ui.routes.home.serverAddress
import com.donut.mixfile.util.cachedMutableOf
import com.donut.mixfile.util.getFileAccessUrl
import com.donut.mixfile.util.showToast
@@ -37,10 +37,18 @@ data class FileDataLog(
category = category.trim()
}
fun isSimilar(other: FileDataLog): Boolean {
return other.shareInfoData.contentEquals(shareInfoData)
}
@get:JSONField(serialize = false)
val downloadUrl: String
get() = getFileAccessUrl(getLocalServerAddress(), shareInfoData, name)
@get:JSONField(serialize = false)
val lanUrl: String
get() = getFileAccessUrl(serverAddress, shareInfoData, name)
fun updateDataList(
list: List<FileDataLog>,
action: (FileDataLog) -> FileDataLog
@@ -98,7 +106,7 @@ data class FileDataLog(
override fun equals(other: Any?): Boolean {
if (other !is FileDataLog) return false
return shareInfoData.contentEquals(other.shareInfoData) && category.contentEquals(other.category)
return isSimilar(other) && category.contentEquals(other.category)
}
}

View File

@@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -21,11 +19,8 @@ import androidx.core.net.toUri
import com.donut.mixfile.activity.video.VideoActivity
import com.donut.mixfile.app
import com.donut.mixfile.currentActivity
import com.donut.mixfile.server.core.utils.bean.MixShareInfo
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
import com.donut.mixfile.server.core.utils.shareCode
import com.donut.mixfile.server.downloadUrl
import com.donut.mixfile.server.lanUrl
import com.donut.mixfile.ui.component.common.MixDialogBuilder
import com.donut.mixfile.ui.routes.favorites.importFileList
import com.donut.mixfile.ui.routes.favorites.openCategorySelect
@@ -36,6 +31,7 @@ import com.donut.mixfile.ui.routes.useSystemPlayer
import com.donut.mixfile.ui.theme.colorScheme
import com.donut.mixfile.util.copyToClipboard
import com.donut.mixfile.util.formatFileSize
import com.donut.mixfile.util.showToast
@OptIn(ExperimentalLayoutApi::class)
@@ -43,24 +39,23 @@ fun showFileInfoDialog(
dataLog: FileDataLog,
onDismiss: () -> Unit = {}
) {
var shareInfo = resolveMixShareInfo(dataLog.shareInfoData)!!
val log = if (favorites.contains(dataLog)) {
dataLog
} else {
favorites.firstOrNull { it.isSimilar(dataLog) }
?: dataLog
}
val shareInfo = resolveMixShareInfo(log.shareInfoData)
if (shareInfo == null) {
showToast("解析文件分享码失败")
return
}
MixDialogBuilder("文件信息", tag = "file-info-${shareInfo.url}").apply {
onDismiss(onDismiss)
setNegativeButton("复制分享码") {
shareInfo.shareCode(useShortCode).copyToClipboard()
}
setContent {
val log = remember(favorites) {
if (favorites.contains(dataLog)) {
dataLog
} else {
favorites.firstOrNull { it.shareInfoData.contentEquals(dataLog.shareInfoData) }
?: dataLog
}
}
LaunchedEffect(log) {
shareInfo = resolveMixShareInfo(log.shareInfoData)!!
}
val fileName = log.name
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
@@ -73,12 +68,12 @@ fun showFileInfoDialog(
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
if (fileName.startsWith("__mixfile_list") || fileName.endsWith(".mix_list")) {
AssistChip(onClick = {
importFileList(shareInfo.downloadUrl)
importFileList(log.downloadUrl)
}, label = {
Text(text = "文件列表", color = colorScheme.primary)
})
}
if (!favorites.any { it.shareInfoData.contentEquals(log.shareInfoData) }) {
if (!favorites.any { it.isSimilar(log) }) {
AssistChip(onClick = {
addFavoriteLog(log)
}, label = {
@@ -117,12 +112,12 @@ fun showFileInfoDialog(
AssistChip(onClick = {
if (useSystemPlayer) {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(shareInfo.downloadUrl.toUri(), "video/*")
intent.setDataAndType(log.downloadUrl.toUri(), "video/*")
currentActivity.startActivity(intent)
return@AssistChip
}
val intent = Intent(app, VideoActivity::class.java).apply {
putExtra("url", shareInfo.downloadUrl)
putExtra("url", log.downloadUrl)
putExtra("hash", shareInfo.url)
}
currentActivity.startActivity(intent)
@@ -132,14 +127,14 @@ fun showFileInfoDialog(
}
if (shareInfo.contentType().startsWith("image/")) {
AssistChip(onClick = {
showImageDialog(shareInfo.downloadUrl)
showImageDialog(log.downloadUrl)
}, label = {
Text(text = "查看图片", color = colorScheme.primary)
})
}
AssistChip(onClick = {
shareInfo.lanUrl.copyToClipboard()
log.lanUrl.copyToClipboard()
}, label = {
Text(text = "复制局域网地址", color = colorScheme.primary)
})
@@ -147,7 +142,7 @@ fun showFileInfoDialog(
}
}
setPositiveButton("下载文件") {
downloadFile(shareInfo)
downloadFile(log)
closeDialog()
showDownloadTaskWindow()
}
@@ -169,7 +164,7 @@ fun InfoText(key: String, value: String) {
}
}
fun downloadFile(shareInfo: MixShareInfo) {
val task = DownloadTask(shareInfo.fileName, shareInfo.fileSize, shareInfo.downloadUrl)
fun downloadFile(file: FileDataLog) {
val task = DownloadTask(file.name, file.size, file.downloadUrl)
task.start()
}

View File

@@ -1,9 +1,8 @@
package com.donut.mixfile
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
import com.alibaba.fastjson2.to
import com.alibaba.fastjson2.toJSONString
import org.junit.Test
import java.net.MalformedURLException
import java.net.URL
import java.util.Date
//appScope.launch(Dispatchers.IO) {
@@ -30,7 +29,11 @@ class ExampleUnitTest {
data class User(
val age: Int,
val date: Date = Date()
)
) {
init {
println("init")
}
}
val map = mapOf(1 to "aa", 2 to "bb")
@@ -38,6 +41,7 @@ class ExampleUnitTest {
@Test
fun main() {
}