mirror of
https://github.com/InvertGeek/MixFile.git
synced 2026-06-03 10:02:07 +08:00
1.15.5
This commit is contained in:
@@ -15,8 +15,8 @@ android {
|
||||
applicationId = "com.donut.mixfile"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 112
|
||||
versionName = "1.15.4"
|
||||
versionCode = 113
|
||||
versionName = "1.15.5"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.donut.mixfile.ui.component.common.CommonColumn
|
||||
import com.donut.mixfile.ui.theme.MainTheme
|
||||
import com.donut.mixfile.ui.theme.colorScheme
|
||||
import com.donut.mixfile.util.file.showFileInfoDialog
|
||||
import com.donut.mixfile.util.file.toDataLog
|
||||
import com.donut.mixfile.util.objects.MixActivity
|
||||
|
||||
class FileDialogActivity : MixActivity("file_dialog") {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.server.core.utils.compressGzip
|
||||
import com.donut.mixfile.server.core.utils.decompressGzip
|
||||
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
|
||||
import io.ktor.http.decodeURLQueryComponent
|
||||
import io.ktor.http.encodeURLPath
|
||||
import java.net.URI
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@@ -85,7 +86,6 @@ open class WebDavManager {
|
||||
val parentPath = normalizedPath.substringBeforeLast('/', "")
|
||||
val name = normalizedPath.substringAfterLast('/')
|
||||
val fileList = WEBDAV_DATA[parentPath] ?: return
|
||||
println("remove ${name} ${fileList.joinToString { it.name }}")
|
||||
synchronized(fileList) {
|
||||
val node = fileList.firstOrNull { it.name.contentEquals(name) }
|
||||
if (node != null) {
|
||||
@@ -151,15 +151,16 @@ fun String?.toDavPath() = normalizePath(this ?: "")
|
||||
|
||||
fun normalizePath(path: String): String {
|
||||
if (path.isBlank()) return ""
|
||||
val encoded = path.encodeURLPath()
|
||||
val uri = try {
|
||||
URI(path)
|
||||
URI(encoded)
|
||||
} catch (_: Exception) {
|
||||
URI("http://dummyhost/${path.encodeURLPath()}").also { uri ->
|
||||
URI("http://dummyhost/${encoded}").also { uri ->
|
||||
if (uri.path == null) return ""
|
||||
}
|
||||
}
|
||||
val cleanPath = uri.path ?: return ""
|
||||
return cleanPath.trim('/').replace(Regex("/+"), "/")
|
||||
return cleanPath.trim('/').replace(Regex("/+"), "/").decodeURLQueryComponent()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ fun String.sanitizeWebDavFileName(): String {
|
||||
return this
|
||||
.replace(illegalChars, " ")
|
||||
.trim()
|
||||
.replace("\\s+".toRegex(), "_")
|
||||
.takeLast(255)
|
||||
.ifEmpty { "unnamed_file" }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.donut.mixfile.server.core.utils.bean
|
||||
|
||||
import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.server.core.utils.compressGzip
|
||||
|
||||
data class FileDataLog(
|
||||
val shareInfoData: String,
|
||||
val name: String,
|
||||
val size: Long,
|
||||
val time: Long = System.currentTimeMillis(),
|
||||
val category: String = "默认",
|
||||
) {
|
||||
|
||||
fun isSimilar(other: FileDataLog): Boolean {
|
||||
return other.shareInfoData.contentEquals(shareInfoData)
|
||||
}
|
||||
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = shareInfoData.hashCode()
|
||||
result = 31 * result + category.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FileDataLog) return false
|
||||
return isSimilar(other) && category.contentEquals(other.category)
|
||||
}
|
||||
}
|
||||
|
||||
fun Collection<FileDataLog>.toByteArray(): ByteArray {
|
||||
val strData = this.toJSONString()
|
||||
val compressedData = compressGzip(strData)
|
||||
return compressedData
|
||||
}
|
||||
@@ -4,134 +4,13 @@ package com.donut.mixfile.server.core.utils.bean
|
||||
import com.alibaba.fastjson2.annotation.JSONField
|
||||
import com.alibaba.fastjson2.to
|
||||
import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.server.core.Uploader
|
||||
import com.donut.mixfile.server.core.aes.decryptAES
|
||||
import com.donut.mixfile.server.core.aes.encryptAES
|
||||
import com.donut.mixfile.server.core.utils.basen.Alphabet
|
||||
import com.donut.mixfile.server.core.utils.basen.BigIntBaseN
|
||||
import com.donut.mixfile.server.core.utils.compressGzip
|
||||
import com.donut.mixfile.server.core.utils.decompressGzip
|
||||
import com.donut.mixfile.server.core.utils.hashMD5
|
||||
import com.donut.mixfile.server.core.utils.hashSHA256
|
||||
import com.donut.mixfile.server.core.utils.parseFileMimeType
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.HttpRequestRetry
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.prepareGet
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.discard
|
||||
|
||||
|
||||
fun ByteArray.hashMixSHA256() = MixShareInfo.ENCODER.encode(hashSHA256())
|
||||
|
||||
data class MixShareInfo(
|
||||
@JSONField(name = "f") val fileName: String,
|
||||
@JSONField(name = "s") val fileSize: Long,
|
||||
@JSONField(name = "h") val headSize: Int,
|
||||
@JSONField(name = "u") val url: String,
|
||||
@JSONField(name = "k") val key: String,
|
||||
@JSONField(name = "r") val referer: String,
|
||||
) {
|
||||
|
||||
@JSONField(serialize = false)
|
||||
var cachedCode: String? = null
|
||||
|
||||
companion object {
|
||||
|
||||
val ENCODER =
|
||||
BigIntBaseN(Alphabet.fromString("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
|
||||
|
||||
fun fromString(string: String) = fromJson(dec(string))
|
||||
|
||||
fun tryFromString(string: String) = try {
|
||||
fromString(string).also {
|
||||
it.cachedCode = string
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun fromJson(json: String): MixShareInfo =
|
||||
json.to()
|
||||
|
||||
private fun enc(input: String): String {
|
||||
val bytes = input.encodeToByteArray()
|
||||
val result = encryptAES(bytes, "123".hashMD5())
|
||||
return ENCODER.encode(result)
|
||||
}
|
||||
|
||||
private fun dec(input: String): String {
|
||||
val bytes = ENCODER.decode(input)
|
||||
val result = decryptAES(bytes, "123".hashMD5())
|
||||
return result!!.decodeToString()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun shareCode() = enc(toJson()).also { cachedCode = it }
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return cachedCode ?: shareCode()
|
||||
}
|
||||
|
||||
private fun toJson(): String = this.toJSONString()
|
||||
|
||||
suspend fun fetchFile(
|
||||
url: String,
|
||||
client: HttpClient,
|
||||
referer: String = this.referer,
|
||||
): ByteArray {
|
||||
val transformedUrl = Uploader.transformUrl(url)
|
||||
val transformedReferer = Uploader.transformReferer(url, referer)
|
||||
val result: ByteArray = client.config {
|
||||
install(HttpRequestRetry) {
|
||||
maxRetries = 3
|
||||
retryOnException(retryOnTimeout = true)
|
||||
retryOnServerErrors()
|
||||
delayMillis { retry ->
|
||||
retry * 100L
|
||||
}
|
||||
}
|
||||
}.prepareGet(transformedUrl) {
|
||||
if (transformedReferer.trim().isNotEmpty()) {
|
||||
header("Referer", transformedReferer)
|
||||
}
|
||||
}.execute {
|
||||
val channel = it.bodyAsChannel()
|
||||
channel.discard(headSize.toLong())
|
||||
decryptAES(channel, ENCODER.decode(key))
|
||||
}
|
||||
val hash = url.split("#").getOrNull(1)
|
||||
if (hash != null) {
|
||||
val currentHash = result.hashMixSHA256()
|
||||
if (!currentHash.contentEquals(hash)) {
|
||||
throw Exception("文件遭到篡改")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is MixShareInfo) {
|
||||
return url == other.url
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun contentType(): String = fileName.parseFileMimeType().toString()
|
||||
|
||||
suspend fun fetchMixFile(client: HttpClient): MixFile {
|
||||
val decryptedBytes = fetchFile(url, client = client)
|
||||
return MixFile.fromBytes(decryptedBytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
data class MixFile(
|
||||
@JSONField(name = "chunk_size") val chunkSize: Long,
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.donut.mixfile.server.core.utils.bean
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField
|
||||
import com.alibaba.fastjson2.to
|
||||
import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.server.core.Uploader
|
||||
import com.donut.mixfile.server.core.aes.decryptAES
|
||||
import com.donut.mixfile.server.core.aes.encryptAES
|
||||
import com.donut.mixfile.server.core.utils.basen.Alphabet
|
||||
import com.donut.mixfile.server.core.utils.basen.BigIntBaseN
|
||||
import com.donut.mixfile.server.core.utils.hashMD5
|
||||
import com.donut.mixfile.server.core.utils.parseFileMimeType
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.HttpRequestRetry
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.prepareGet
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.discard
|
||||
|
||||
data class MixShareInfo(
|
||||
@JSONField(name = "f") val fileName: String,
|
||||
@JSONField(name = "s") val fileSize: Long,
|
||||
@JSONField(name = "h") val headSize: Int,
|
||||
@JSONField(name = "u") val url: String,
|
||||
@JSONField(name = "k") val key: String,
|
||||
@JSONField(name = "r") val referer: String,
|
||||
) {
|
||||
|
||||
@JSONField(serialize = false)
|
||||
var cachedCode: String? = null
|
||||
|
||||
companion object {
|
||||
|
||||
val ENCODER =
|
||||
BigIntBaseN(Alphabet.fromString("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
|
||||
|
||||
fun fromString(string: String) = fromJson(dec(string))
|
||||
|
||||
fun tryFromString(string: String) = try {
|
||||
fromString(string).also {
|
||||
it.cachedCode = string
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun fromJson(json: String): MixShareInfo =
|
||||
json.to()
|
||||
|
||||
private fun enc(input: String): String {
|
||||
val bytes = input.encodeToByteArray()
|
||||
val result = encryptAES(bytes, "123".hashMD5())
|
||||
return ENCODER.encode(result)
|
||||
}
|
||||
|
||||
private fun dec(input: String): String {
|
||||
val bytes = ENCODER.decode(input)
|
||||
val result = decryptAES(bytes, "123".hashMD5())
|
||||
return result!!.decodeToString()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun shareCode() = enc(toJson()).also { cachedCode = it }
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return cachedCode ?: shareCode()
|
||||
}
|
||||
|
||||
fun toDataLog(): FileDataLog {
|
||||
return FileDataLog(
|
||||
shareInfoData = this.toString(),
|
||||
name = this.fileName,
|
||||
size = this.fileSize
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun toJson(): String = this.toJSONString()
|
||||
|
||||
suspend fun fetchFile(
|
||||
url: String,
|
||||
client: HttpClient,
|
||||
referer: String = this.referer,
|
||||
): ByteArray {
|
||||
val transformedUrl = Uploader.transformUrl(url)
|
||||
val transformedReferer = Uploader.transformReferer(url, referer)
|
||||
val result: ByteArray = client.config {
|
||||
install(HttpRequestRetry) {
|
||||
maxRetries = 3
|
||||
retryOnException(retryOnTimeout = true)
|
||||
retryOnServerErrors()
|
||||
delayMillis { retry ->
|
||||
retry * 100L
|
||||
}
|
||||
}
|
||||
}.prepareGet(transformedUrl) {
|
||||
if (transformedReferer.trim().isNotEmpty()) {
|
||||
header("Referer", transformedReferer)
|
||||
}
|
||||
}.execute {
|
||||
val channel = it.bodyAsChannel()
|
||||
channel.discard(headSize.toLong())
|
||||
decryptAES(channel, ENCODER.decode(key))
|
||||
}
|
||||
val hash = url.split("#").getOrNull(1)
|
||||
if (hash != null) {
|
||||
val currentHash = result.hashMixSHA256()
|
||||
if (!currentHash.contentEquals(hash)) {
|
||||
throw Exception("文件遭到篡改")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is MixShareInfo) {
|
||||
return url == other.url
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun contentType(): String = fileName.parseFileMimeType().toString()
|
||||
|
||||
suspend fun fetchMixFile(client: HttpClient): MixFile {
|
||||
val decryptedBytes = fetchFile(url, client = client)
|
||||
return MixFile.fromBytes(decryptedBytes)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ 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.bean.FileDataLog
|
||||
import com.donut.mixfile.ui.component.common.MixDialogBuilder
|
||||
import com.donut.mixfile.ui.component.common.SingleSelectItemList
|
||||
import com.donut.mixfile.ui.nav.MixNavPage
|
||||
@@ -46,7 +47,6 @@ 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
|
||||
import com.donut.mixfile.util.file.favorites
|
||||
import com.donut.mixfile.util.file.selectAndUploadFile
|
||||
|
||||
@@ -41,7 +41,6 @@ import com.donut.mixfile.util.file.deleteUploadLog
|
||||
import com.donut.mixfile.util.file.selectAndUploadFile
|
||||
import com.donut.mixfile.util.file.showFileInfoDialog
|
||||
import com.donut.mixfile.util.file.showFileList
|
||||
import com.donut.mixfile.util.file.toDataLog
|
||||
import com.donut.mixfile.util.file.uploadLogs
|
||||
import com.donut.mixfile.util.getIpAddressInLocalNetwork
|
||||
import com.donut.mixfile.util.readClipBoardText
|
||||
|
||||
@@ -21,10 +21,9 @@ import com.donut.mixfile.ui.theme.colorScheme
|
||||
import com.donut.mixfile.util.AsyncEffect
|
||||
import com.donut.mixfile.util.errorDialog
|
||||
import com.donut.mixfile.util.file.doUploadFile
|
||||
import com.donut.mixfile.util.file.downloadUrl
|
||||
import com.donut.mixfile.util.file.loadDataWithMaxSize
|
||||
import com.donut.mixfile.util.file.loadFileList
|
||||
|
||||
import com.donut.mixfile.util.file.toDataLog
|
||||
import com.donut.mixfile.util.formatFileSize
|
||||
import com.donut.mixfile.util.getCurrentTime
|
||||
import com.donut.mixfile.util.objects.ProgressContent
|
||||
|
||||
@@ -35,6 +35,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||
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.bean.FileDataLog
|
||||
import com.donut.mixfile.server.core.utils.parseFileMimeType
|
||||
import com.donut.mixfile.server.serverStarted
|
||||
import com.donut.mixfile.ui.theme.colorScheme
|
||||
|
||||
@@ -7,7 +7,7 @@ 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.FileDataLog
|
||||
import com.donut.mixfile.server.core.utils.bean.MixShareInfo
|
||||
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
|
||||
import com.donut.mixfile.ui.component.common.MixDialogBuilder
|
||||
@@ -20,84 +20,59 @@ import com.donut.mixfile.util.getFileAccessUrl
|
||||
import com.donut.mixfile.util.showToast
|
||||
|
||||
|
||||
data class FileDataLog(
|
||||
val shareInfoData: String,
|
||||
val name: String,
|
||||
val size: Long,
|
||||
val time: Long = System.currentTimeMillis(),
|
||||
val category: String = currentCategory.ifEmpty { "默认" },
|
||||
) {
|
||||
val FileDataLog.downloadUrl: String
|
||||
get() = getFileAccessUrl(getLocalServerAddress(), shareInfoData, name)
|
||||
|
||||
fun isSimilar(other: FileDataLog): Boolean {
|
||||
return other.shareInfoData.contentEquals(shareInfoData)
|
||||
|
||||
val FileDataLog.lanUrl: String
|
||||
get() = getFileAccessUrl(serverAddress, shareInfoData, name)
|
||||
|
||||
fun FileDataLog.updateDataList(
|
||||
list: List<FileDataLog>,
|
||||
action: (FileDataLog) -> FileDataLog
|
||||
): List<FileDataLog> {
|
||||
val index = list.indexOf(this)
|
||||
if (index == -1) {
|
||||
return list
|
||||
}
|
||||
val result = list.toMutableList()
|
||||
result[index] = action(this)
|
||||
return result
|
||||
}
|
||||
|
||||
@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
|
||||
): List<FileDataLog> {
|
||||
val index = list.indexOf(this)
|
||||
if (index == -1) {
|
||||
return list
|
||||
fun FileDataLog.rename(callback: (FileDataLog) -> Unit = {}) {
|
||||
var shareInfo = resolveMixShareInfo(shareInfoData) ?: return
|
||||
MixDialogBuilder("重命名文件").apply {
|
||||
var name by mutableStateOf(shareInfo.fileName)
|
||||
setContent {
|
||||
OutlinedTextField(value = name, onValueChange = {
|
||||
name = it
|
||||
}, modifier = Modifier.fillMaxWidth(), label = {
|
||||
Text(text = "输入文件名")
|
||||
})
|
||||
}
|
||||
val result = list.toMutableList()
|
||||
result[index] = action(this)
|
||||
return result
|
||||
}
|
||||
|
||||
fun rename(callback: (FileDataLog) -> Unit = {}) {
|
||||
var shareInfo = resolveMixShareInfo(shareInfoData) ?: return
|
||||
MixDialogBuilder("重命名文件").apply {
|
||||
var name by mutableStateOf(shareInfo.fileName)
|
||||
setContent {
|
||||
OutlinedTextField(value = name, onValueChange = {
|
||||
name = it
|
||||
}, modifier = Modifier.fillMaxWidth(), label = {
|
||||
Text(text = "输入文件名")
|
||||
})
|
||||
setDefaultNegative()
|
||||
setPositiveButton("确定") {
|
||||
if (name.isEmpty()) {
|
||||
showToast("文件名不能为空!")
|
||||
return@setPositiveButton
|
||||
}
|
||||
setDefaultNegative()
|
||||
setPositiveButton("确定") {
|
||||
if (name.isEmpty()) {
|
||||
showToast("文件名不能为空!")
|
||||
return@setPositiveButton
|
||||
}
|
||||
shareInfo = shareInfo.copy(fileName = name)
|
||||
val renamedLog = copy(
|
||||
name = name,
|
||||
shareInfoData = shareInfo.toString()
|
||||
)
|
||||
favorites = updateDataList(favorites) {
|
||||
renamedLog
|
||||
}
|
||||
uploadLogs = updateDataList(uploadLogs) {
|
||||
renamedLog
|
||||
}
|
||||
callback(renamedLog)
|
||||
showToast("重命名文件成功!")
|
||||
closeDialog()
|
||||
shareInfo = shareInfo.copy(fileName = name)
|
||||
val renamedLog = copy(
|
||||
name = name,
|
||||
shareInfoData = shareInfo.toString()
|
||||
)
|
||||
favorites = updateDataList(favorites) {
|
||||
renamedLog
|
||||
}
|
||||
show()
|
||||
uploadLogs = updateDataList(uploadLogs) {
|
||||
renamedLog
|
||||
}
|
||||
callback(renamedLog)
|
||||
showToast("重命名文件成功!")
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = shareInfoData.hashCode()
|
||||
result = 31 * result + category.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FileDataLog) return false
|
||||
return isSimilar(other) && category.contentEquals(other.category)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +122,6 @@ fun addFavoriteLog(
|
||||
return true
|
||||
}
|
||||
|
||||
fun MixShareInfo.toDataLog(): FileDataLog {
|
||||
return FileDataLog(
|
||||
shareInfoData = this.toString(),
|
||||
name = this.fileName,
|
||||
size = this.fileSize
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteFavoriteLog(uploadLog: FileDataLog, callback: () -> Unit = {}) {
|
||||
MixDialogBuilder("确定删除?").apply {
|
||||
|
||||
@@ -19,6 +19,7 @@ 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.FileDataLog
|
||||
import com.donut.mixfile.server.core.utils.hashSHA256
|
||||
import com.donut.mixfile.server.core.utils.resolveMixShareInfo
|
||||
import com.donut.mixfile.server.core.utils.shareCode
|
||||
|
||||
@@ -12,11 +12,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.alibaba.fastjson2.into
|
||||
import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.activity.video.VideoActivity
|
||||
import com.donut.mixfile.app
|
||||
import com.donut.mixfile.currentActivity
|
||||
import com.donut.mixfile.server.core.utils.compressGzip
|
||||
import com.donut.mixfile.server.core.utils.bean.FileDataLog
|
||||
import com.donut.mixfile.server.core.utils.bean.toByteArray
|
||||
import com.donut.mixfile.server.core.utils.decompressGzip
|
||||
import com.donut.mixfile.server.core.utils.hashSHA256
|
||||
import com.donut.mixfile.server.core.utils.parseFileMimeType
|
||||
@@ -32,10 +32,8 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
fun exportFileList(fileList: Collection<FileDataLog>, name: String) {
|
||||
val strData = fileList.toJSONString()
|
||||
val compressedData = compressGzip(strData)
|
||||
doUploadFile(
|
||||
compressedData,
|
||||
fileList.toByteArray(),
|
||||
"${name}.mix_list",
|
||||
false
|
||||
)
|
||||
|
||||
@@ -237,7 +237,6 @@ fun String.sanitizeFileName(): String {
|
||||
return this
|
||||
.replace(illegalChars, " ")
|
||||
.trim()
|
||||
.replace("\\s+".toRegex(), "_")
|
||||
.takeLast(255)
|
||||
.ifEmpty { "unnamed_file" }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package com.donut.mixfile
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField
|
||||
import com.alibaba.fastjson2.to
|
||||
import com.alibaba.fastjson2.toJSONString
|
||||
import com.donut.mixfile.server.core.routes.api.webdav.utils.normalizePath
|
||||
import com.donut.mixfile.server.core.utils.isValidURL
|
||||
import org.junit.Test
|
||||
import java.util.Date
|
||||
import java.net.URI
|
||||
|
||||
//appScope.launch(Dispatchers.IO) {
|
||||
// repeat(100) {
|
||||
|
||||
Reference in New Issue
Block a user