что-то
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException
|
||||
import com.fasterxml.jackson.core.JsonParseException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalDirectory
|
||||
@@ -11,7 +10,6 @@ import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.DataPage
|
||||
import com.github.nullptroma.wallenc.domain.models.IDirectory
|
||||
import com.github.nullptroma.wallenc.domain.models.IFile
|
||||
import com.github.nullptroma.wallenc.domain.models.IMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.models.IStorageAccessor
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -40,7 +38,6 @@ class LocalStorageAccessor(
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) : IStorageAccessor {
|
||||
private val _absolutePath: Path = Path(absolutePath).normalize().absolute()
|
||||
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
|
||||
private val _size = MutableStateFlow<Long?>(null)
|
||||
override val size: StateFlow<Long?> = _size
|
||||
@@ -61,7 +58,7 @@ class LocalStorageAccessor(
|
||||
// запускам сканирование хранилища
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
Timber.d("Local storage path: $_absolutePath")
|
||||
updateSizeAndNumOfFiles()
|
||||
scanSizeAndNumOfFiles()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,12 +104,106 @@ class LocalStorageAccessor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (useCallbackForSelf) {
|
||||
} else if (useCallbackForSelf) {
|
||||
callback(dir)
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalStorageFilePair private constructor(
|
||||
val file: File,
|
||||
val metaFile: File,
|
||||
val meta: LocalMetaInfo
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
|
||||
fun fromFile(basePath: Path, file: File): LocalStorageFilePair? {
|
||||
if (!file.exists())
|
||||
return null
|
||||
if (file.name.endsWith(META_INFO_POSTFIX))
|
||||
return fromMetaFile(basePath, file)
|
||||
|
||||
val filePath = file.toPath()
|
||||
val metaFilePath = Path(
|
||||
if (file.isFile) {
|
||||
file.absolutePath + META_INFO_POSTFIX
|
||||
} else {
|
||||
Path(file.absolutePath, META_INFO_POSTFIX).pathString
|
||||
}
|
||||
)
|
||||
val metaFile = metaFilePath.toFile()
|
||||
val metaInfo: LocalMetaInfo
|
||||
val storageFilePath = "/" + filePath.relativeTo(basePath)
|
||||
|
||||
if (!metaFile.exists()) {
|
||||
metaInfo = LocalMetaInfo(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
_jackson.writeValue(metaFile, metaInfo)
|
||||
} else {
|
||||
var readMeta: LocalMetaInfo
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
readMeta = _jackson.readValue(reader)
|
||||
} catch (e: JacksonException) {
|
||||
// если файл повреждён - пересоздать
|
||||
readMeta = LocalMetaInfo(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
_jackson.writeValue(metaFile, readMeta)
|
||||
}
|
||||
metaInfo = readMeta
|
||||
}
|
||||
return LocalStorageFilePair(
|
||||
file = file,
|
||||
metaFile = metaFile,
|
||||
meta = metaInfo
|
||||
)
|
||||
}
|
||||
|
||||
fun fromMetaFile(basePath: Path, metaFile: File): LocalStorageFilePair? {
|
||||
if (!metaFile.exists())
|
||||
return null
|
||||
if (!metaFile.name.endsWith(META_INFO_POSTFIX))
|
||||
return fromFile(basePath, metaFile)
|
||||
var pair: LocalStorageFilePair? = null
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
val metaInfo: LocalMetaInfo = _jackson.readValue(reader)
|
||||
val pathString = Path(basePath.pathString, metaInfo.path).pathString
|
||||
val file = File(pathString)
|
||||
if (!file.exists()) {
|
||||
metaFile.delete()
|
||||
} else {
|
||||
pair = LocalStorageFilePair(
|
||||
file = file,
|
||||
metaFile = metaFile,
|
||||
meta = metaInfo
|
||||
)
|
||||
}
|
||||
} catch (e: JacksonException) {
|
||||
metaFile.delete()
|
||||
}
|
||||
return pair
|
||||
}
|
||||
|
||||
fun from(basePath: Path, anyFile: File): LocalStorageFilePair? {
|
||||
return if (anyFile.name.endsWith(META_INFO_POSTFIX))
|
||||
fromMetaFile(basePath, anyFile)
|
||||
else
|
||||
fromFile(basePath, anyFile)
|
||||
}
|
||||
|
||||
fun from(basePath: Path, storagePath: String): LocalStorageFilePair? {
|
||||
val filePath = Path(basePath.pathString, storagePath)
|
||||
return from(basePath, filePath.toFile())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Перебирает все файлы и каталоги в relativePath и возвращает с мета-информацией
|
||||
*
|
||||
@@ -128,101 +219,33 @@ class LocalStorageAccessor(
|
||||
val basePath = Path(_absolutePath.pathString, baseStoragePath)
|
||||
val workedFiles = mutableSetOf<String>()
|
||||
val workedMetaFiles = mutableSetOf<String>()
|
||||
var count = 0
|
||||
|
||||
scanFileSystem(basePath.toFile(), maxDepth, { file ->
|
||||
// Если парный файл уже был обработан - скип. Это позволит не читать metaFile дважды
|
||||
if(workedFiles.contains(file.absolutePath) || workedMetaFiles.contains(file.absolutePath)) {
|
||||
count++
|
||||
if (workedFiles.contains(file.absolutePath) || workedMetaFiles.contains(file.absolutePath)) {
|
||||
return@scanFileSystem
|
||||
}
|
||||
|
||||
val filePath = Path(file.absolutePath)
|
||||
val pair = LocalStorageFilePair.from(_absolutePath, file)
|
||||
if(pair != null) {
|
||||
workedFiles.add(pair.file.absolutePath)
|
||||
workedMetaFiles.add(pair.metaFile.absolutePath)
|
||||
|
||||
// если это файл с мета-информацией - пропустить
|
||||
if (filePath.pathString.endsWith(META_INFO_POSTFIX)) {
|
||||
// Если не удаётся прочитать метаданные или они указывают на несуществующий файл - удалить
|
||||
try {
|
||||
val reader = file.bufferedReader()
|
||||
val meta : LocalMetaInfo = _jackson.readValue(reader)
|
||||
val pathString = Path(_absolutePath.pathString, meta.path).pathString
|
||||
val originalFile = File(pathString)
|
||||
if (!originalFile.exists())
|
||||
file.delete()
|
||||
|
||||
// если успешно прочитано - отправить колбек и добавить обработанный файл
|
||||
workedFiles.add(pathString)
|
||||
workedMetaFiles.add(file.absolutePath)
|
||||
if (file.isFile) {
|
||||
fileCallback?.invoke(originalFile, LocalFile(meta))
|
||||
} else {
|
||||
dirCallback?.invoke(originalFile, LocalDirectory(meta, null))
|
||||
}
|
||||
} catch (e: JacksonException) {
|
||||
file.delete()
|
||||
}
|
||||
|
||||
return@scanFileSystem
|
||||
}
|
||||
else {
|
||||
val metaFilePath = Path(
|
||||
if (file.isFile) {
|
||||
file.absolutePath + META_INFO_POSTFIX
|
||||
} else {
|
||||
Path(file.absolutePath, META_INFO_POSTFIX).pathString
|
||||
}
|
||||
)
|
||||
val metaFile = metaFilePath.toFile()
|
||||
val metaInfo: LocalMetaInfo
|
||||
val storageFilePath = "/" + filePath.relativeTo(_absolutePath)
|
||||
|
||||
if (!metaFile.exists()) {
|
||||
metaInfo = createNewLocalMetaInfo(storageFilePath, filePath.fileSize())
|
||||
_jackson.writeValue(metaFile, metaInfo)
|
||||
if (pair.file.isFile) {
|
||||
fileCallback?.invoke(pair.file, LocalFile(pair.meta))
|
||||
} else {
|
||||
var readMeta: LocalMetaInfo
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
readMeta = _jackson.readValue(reader)
|
||||
} catch (e: JacksonException) {
|
||||
// если файл повреждён - пересоздать
|
||||
readMeta = createNewLocalMetaInfo(storageFilePath, filePath.fileSize())
|
||||
_jackson.writeValue(metaFile, readMeta)
|
||||
}
|
||||
metaInfo = readMeta
|
||||
}
|
||||
|
||||
workedFiles.add(file.absolutePath)
|
||||
workedMetaFiles.add(metaFile.absolutePath)
|
||||
if (file.isFile) {
|
||||
fileCallback?.invoke(file, LocalFile(metaInfo))
|
||||
} else {
|
||||
dirCallback?.invoke(file, LocalDirectory(metaInfo, null))
|
||||
dirCallback?.invoke(pair.file, LocalDirectory(pair.meta, null))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт LocalMetaInfo, не требуя наличие файла в файловой системе
|
||||
* @param storagePath полный путь в Storage
|
||||
* @param size размер файла
|
||||
*/
|
||||
private fun createNewLocalMetaInfo(storagePath: String, size: Long): LocalMetaInfo {
|
||||
return LocalMetaInfo(
|
||||
size = size,
|
||||
isDeleted = false,
|
||||
isHidden = false,
|
||||
lastModified = java.time.Clock.systemUTC().instant(),
|
||||
path = storagePath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Считает файлы и их размер. Не бросает исключения, если файлы недоступны
|
||||
* @throws none Если возникла ошибка, оставляет размер и количества файлов равными null
|
||||
*/
|
||||
private suspend fun updateSizeAndNumOfFiles() {
|
||||
private suspend fun scanSizeAndNumOfFiles() {
|
||||
if (!checkAvailable()) {
|
||||
_size.value = null
|
||||
_numberOfFiles.value = null
|
||||
@@ -235,6 +258,11 @@ class LocalStorageAccessor(
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, localFile ->
|
||||
size += localFile.metaInfo.size
|
||||
numOfFiles++
|
||||
|
||||
if(numOfFiles % DATA_PAGE_LENGTH == 0) {
|
||||
_size.value = size
|
||||
_numberOfFiles.value = numOfFiles
|
||||
}
|
||||
})
|
||||
|
||||
_size.value = size
|
||||
@@ -270,7 +298,7 @@ class LocalStorageAccessor(
|
||||
val buf = mutableListOf<IFile>()
|
||||
var pageNumber = 0
|
||||
scanStorage(baseStoragePath = path, maxDepth = 0, fileCallback = { _, localFile ->
|
||||
if(buf.size == DATA_PAGE_LENGTH) {
|
||||
if (buf.size == DATA_PAGE_LENGTH) {
|
||||
val page = DataPage(
|
||||
list = buf.toList(),
|
||||
isLoading = false,
|
||||
@@ -297,7 +325,14 @@ class LocalStorageAccessor(
|
||||
}.flowOn(ioDispatcher)
|
||||
|
||||
override suspend fun getAllDirs(): List<IDirectory> = withContext(ioDispatcher) {
|
||||
TODO("Not yet implemented")
|
||||
if (!checkAvailable())
|
||||
return@withContext listOf()
|
||||
|
||||
val list = mutableListOf<IDirectory>()
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, dirCallback = { _, localDir ->
|
||||
list.add(localDir)
|
||||
})
|
||||
return@withContext list
|
||||
}
|
||||
|
||||
override suspend fun getDirs(path: String): List<IDirectory> = withContext(ioDispatcher) {
|
||||
|
||||
@@ -6,8 +6,8 @@ import java.time.Instant
|
||||
|
||||
data class LocalMetaInfo(
|
||||
override val size: Long,
|
||||
override val isDeleted: Boolean,
|
||||
override val isHidden: Boolean,
|
||||
override val lastModified: Instant,
|
||||
override val isDeleted: Boolean = false,
|
||||
override val isHidden: Boolean = false,
|
||||
override val lastModified: Instant = java.time.Clock.systemUTC().instant(),
|
||||
override val path: String
|
||||
) : IMetaInfo
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.nullptroma.wallenc.domain.usecases
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.models.IDirectory
|
||||
import com.github.nullptroma.wallenc.domain.models.IFile
|
||||
import com.github.nullptroma.wallenc.domain.models.IStorage
|
||||
|
||||
@@ -14,4 +15,9 @@ class StorageFileManagementUseCase {
|
||||
val storage = _storage ?: return listOf()
|
||||
return storage.accessor.getAllFiles()
|
||||
}
|
||||
|
||||
suspend fun getAllDirs(): List<IDirectory> {
|
||||
val storage = _storage ?: return listOf()
|
||||
return storage.accessor.getAllDirs()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.nullptroma.wallenc.domain.models.IDirectory
|
||||
import com.github.nullptroma.wallenc.domain.models.IFile
|
||||
import com.github.nullptroma.wallenc.domain.models.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.usecases.GetAllRawStoragesUseCase
|
||||
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
|
||||
@@ -9,6 +13,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@HiltViewModel
|
||||
class LocalVaultViewModel @Inject constructor(
|
||||
@@ -30,11 +35,21 @@ class LocalVaultViewModel @Inject constructor(
|
||||
fun printAllFilesToLog(storage: IStorage) {
|
||||
_storageFileManagementUseCase.setStorage(storage)
|
||||
viewModelScope.launch {
|
||||
val files = _storageFileManagementUseCase.getAllFiles()
|
||||
val files: List<IFile>
|
||||
val dirs: List<IDirectory>
|
||||
val time = measureTimeMillis {
|
||||
files = _storageFileManagementUseCase.getAllFiles()
|
||||
dirs = _storageFileManagementUseCase.getAllDirs()
|
||||
}
|
||||
for (file in files) {
|
||||
Timber.tag("File")
|
||||
Timber.tag("Files")
|
||||
Timber.d(file.metaInfo.toString())
|
||||
}
|
||||
for (dir in dirs) {
|
||||
Timber.tag("Dirs")
|
||||
Timber.d(dir.metaInfo.toString())
|
||||
}
|
||||
Timber.d("Time: $time ms")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user