Local готово
This commit is contained in:
@@ -26,6 +26,7 @@ import timber.log.Timber
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
import kotlin.io.path.absolute
|
||||||
@@ -34,10 +35,11 @@ import kotlin.io.path.pathString
|
|||||||
import kotlin.io.path.relativeTo
|
import kotlin.io.path.relativeTo
|
||||||
|
|
||||||
class LocalStorageAccessor(
|
class LocalStorageAccessor(
|
||||||
absolutePath: String,
|
filesystemBasePath: String,
|
||||||
private val ioDispatcher: CoroutineDispatcher
|
private val ioDispatcher: CoroutineDispatcher
|
||||||
) : IStorageAccessor {
|
) : IStorageAccessor {
|
||||||
private val _absolutePath: Path = Path(absolutePath).normalize().absolute()
|
private val _filesystemBasePath: Path = Path(filesystemBasePath).normalize().absolute()
|
||||||
|
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||||
|
|
||||||
private val _size = MutableStateFlow<Long?>(null)
|
private val _size = MutableStateFlow<Long?>(null)
|
||||||
override val size: StateFlow<Long?> = _size
|
override val size: StateFlow<Long?> = _size
|
||||||
@@ -57,7 +59,7 @@ class LocalStorageAccessor(
|
|||||||
init {
|
init {
|
||||||
// запускам сканирование хранилища
|
// запускам сканирование хранилища
|
||||||
CoroutineScope(ioDispatcher).launch {
|
CoroutineScope(ioDispatcher).launch {
|
||||||
Timber.d("Local storage path: $_absolutePath")
|
Timber.d("Local storage path: $_filesystemBasePath")
|
||||||
scanSizeAndNumOfFiles()
|
scanSizeAndNumOfFiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,7 @@ class LocalStorageAccessor(
|
|||||||
* Проверяет существование корневого пути Storage в файловой системе, изменяет _isAvailable
|
* Проверяет существование корневого пути Storage в файловой системе, изменяет _isAvailable
|
||||||
*/
|
*/
|
||||||
private fun checkAvailable(): Boolean {
|
private fun checkAvailable(): Boolean {
|
||||||
_isAvailable.value = _absolutePath.toFile().exists()
|
_isAvailable.value = _filesystemBasePath.toFile().exists()
|
||||||
return _isAvailable.value
|
return _isAvailable.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +120,11 @@ class LocalStorageAccessor(
|
|||||||
companion object {
|
companion object {
|
||||||
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||||
|
|
||||||
fun fromFile(basePath: Path, file: File): LocalStorageFilePair? {
|
fun fromFile(filesystemBasePath: Path, file: File): LocalStorageFilePair? {
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
return null
|
return null
|
||||||
if (file.name.endsWith(META_INFO_POSTFIX))
|
if (file.name.endsWith(META_INFO_POSTFIX))
|
||||||
return fromMetaFile(basePath, file)
|
return fromMetaFile(filesystemBasePath, file)
|
||||||
|
|
||||||
val filePath = file.toPath()
|
val filePath = file.toPath()
|
||||||
val metaFilePath = Path(
|
val metaFilePath = Path(
|
||||||
@@ -134,7 +136,7 @@ class LocalStorageAccessor(
|
|||||||
)
|
)
|
||||||
val metaFile = metaFilePath.toFile()
|
val metaFile = metaFilePath.toFile()
|
||||||
val metaInfo: LocalMetaInfo
|
val metaInfo: LocalMetaInfo
|
||||||
val storageFilePath = "/" + filePath.relativeTo(basePath)
|
val storageFilePath = "/" + filePath.relativeTo(filesystemBasePath)
|
||||||
|
|
||||||
if (!metaFile.exists()) {
|
if (!metaFile.exists()) {
|
||||||
metaInfo = LocalMetaInfo(
|
metaInfo = LocalMetaInfo(
|
||||||
@@ -164,16 +166,16 @@ class LocalStorageAccessor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromMetaFile(basePath: Path, metaFile: File): LocalStorageFilePair? {
|
fun fromMetaFile(filesystemBasePath: Path, metaFile: File): LocalStorageFilePair? {
|
||||||
if (!metaFile.exists())
|
if (!metaFile.exists())
|
||||||
return null
|
return null
|
||||||
if (!metaFile.name.endsWith(META_INFO_POSTFIX))
|
if (!metaFile.name.endsWith(META_INFO_POSTFIX))
|
||||||
return fromFile(basePath, metaFile)
|
return fromFile(filesystemBasePath, metaFile)
|
||||||
var pair: LocalStorageFilePair? = null
|
var pair: LocalStorageFilePair? = null
|
||||||
try {
|
try {
|
||||||
val reader = metaFile.bufferedReader()
|
val reader = metaFile.bufferedReader()
|
||||||
val metaInfo: LocalMetaInfo = _jackson.readValue(reader)
|
val metaInfo: LocalMetaInfo = _jackson.readValue(reader)
|
||||||
val pathString = Path(basePath.pathString, metaInfo.path).pathString
|
val pathString = Path(filesystemBasePath.pathString, metaInfo.path).pathString
|
||||||
val file = File(pathString)
|
val file = File(pathString)
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
metaFile.delete()
|
metaFile.delete()
|
||||||
@@ -190,16 +192,16 @@ class LocalStorageAccessor(
|
|||||||
return pair
|
return pair
|
||||||
}
|
}
|
||||||
|
|
||||||
fun from(basePath: Path, anyFile: File): LocalStorageFilePair? {
|
fun from(filesystemBasePath: Path, anyFile: File): LocalStorageFilePair? {
|
||||||
return if (anyFile.name.endsWith(META_INFO_POSTFIX))
|
return if (anyFile.name.endsWith(META_INFO_POSTFIX))
|
||||||
fromMetaFile(basePath, anyFile)
|
fromMetaFile(filesystemBasePath, anyFile)
|
||||||
else
|
else
|
||||||
fromFile(basePath, anyFile)
|
fromFile(filesystemBasePath, anyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun from(basePath: Path, storagePath: String): LocalStorageFilePair? {
|
fun from(filesystemBasePath: Path, storagePath: String): LocalStorageFilePair? {
|
||||||
val filePath = Path(basePath.pathString, storagePath)
|
val filePath = Path(filesystemBasePath.pathString, storagePath)
|
||||||
return from(basePath, filePath.toFile())
|
return from(filesystemBasePath, filePath.toFile())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +218,7 @@ class LocalStorageAccessor(
|
|||||||
) {
|
) {
|
||||||
if (!checkAvailable())
|
if (!checkAvailable())
|
||||||
throw Exception("Not available")
|
throw Exception("Not available")
|
||||||
val basePath = Path(_absolutePath.pathString, baseStoragePath)
|
val basePath = Path(_filesystemBasePath.pathString, baseStoragePath)
|
||||||
val workedFiles = mutableSetOf<String>()
|
val workedFiles = mutableSetOf<String>()
|
||||||
val workedMetaFiles = mutableSetOf<String>()
|
val workedMetaFiles = mutableSetOf<String>()
|
||||||
|
|
||||||
@@ -226,7 +228,7 @@ class LocalStorageAccessor(
|
|||||||
return@scanFileSystem
|
return@scanFileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
val pair = LocalStorageFilePair.from(_absolutePath, file)
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, file)
|
||||||
if(pair != null) {
|
if(pair != null) {
|
||||||
workedFiles.add(pair.file.absolutePath)
|
workedFiles.add(pair.file.absolutePath)
|
||||||
workedMetaFiles.add(pair.metaFile.absolutePath)
|
workedMetaFiles.add(pair.metaFile.absolutePath)
|
||||||
@@ -336,43 +338,116 @@ class LocalStorageAccessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDirs(path: String): List<IDirectory> = withContext(ioDispatcher) {
|
override suspend fun getDirs(path: String): List<IDirectory> = withContext(ioDispatcher) {
|
||||||
TODO("Not yet implemented")
|
if (!checkAvailable())
|
||||||
|
return@withContext listOf()
|
||||||
|
|
||||||
|
val list = mutableListOf<IDirectory>()
|
||||||
|
scanStorage(baseStoragePath = path, maxDepth = 0, dirCallback = { _, localDir ->
|
||||||
|
list.add(localDir)
|
||||||
|
})
|
||||||
|
return@withContext list
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDirsFlow(path: String): Flow<DataPackage<List<IDirectory>>> {
|
override fun getDirsFlow(path: String): Flow<DataPackage<List<IDirectory>>> = flow {
|
||||||
TODO("Not yet implemented")
|
if (!checkAvailable())
|
||||||
|
return@flow
|
||||||
|
|
||||||
|
val buf = mutableListOf<IDirectory>()
|
||||||
|
var pageNumber = 0
|
||||||
|
scanStorage(baseStoragePath = path, maxDepth = 0, dirCallback = { _, localDir ->
|
||||||
|
if (buf.size == DATA_PAGE_LENGTH) {
|
||||||
|
val page = DataPage(
|
||||||
|
list = buf.toList(),
|
||||||
|
isLoading = false,
|
||||||
|
isError = false,
|
||||||
|
hasNext = true,
|
||||||
|
pageLength = DATA_PAGE_LENGTH,
|
||||||
|
pageIndex = pageNumber++
|
||||||
|
)
|
||||||
|
emit(page)
|
||||||
|
buf.clear()
|
||||||
|
}
|
||||||
|
buf.add(localDir)
|
||||||
|
})
|
||||||
|
// отправка последней страницы
|
||||||
|
val page = DataPage(
|
||||||
|
list = buf.toList(),
|
||||||
|
isLoading = false,
|
||||||
|
isError = false,
|
||||||
|
hasNext = false,
|
||||||
|
pageLength = DATA_PAGE_LENGTH,
|
||||||
|
pageIndex = pageNumber++
|
||||||
|
)
|
||||||
|
emit(page)
|
||||||
|
}.flowOn(ioDispatcher)
|
||||||
|
|
||||||
|
private fun writeMeta(metaFile: File, meta: LocalMetaInfo) {
|
||||||
|
_jackson.writeValue(metaFile, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun touchFile(path: String) = withContext(ioDispatcher) {
|
private fun createFile(storagePath: String): LocalFile {
|
||||||
TODO("Not yet implemented")
|
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||||
|
val file = path.toFile()
|
||||||
|
if(file.exists() && file.isDirectory) {
|
||||||
|
throw Exception("Что то пошло не так") // TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
file.createNewFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun touchDir(path: String) = withContext(ioDispatcher) {
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO
|
||||||
TODO("Not yet implemented")
|
val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant())
|
||||||
|
writeMeta(pair.metaFile, newMeta)
|
||||||
|
return LocalFile(newMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDir(storagePath: String): LocalDirectory {
|
||||||
|
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||||
|
val file = path.toFile()
|
||||||
|
if(file.exists() && !file.isDirectory) {
|
||||||
|
throw Exception("Что то пошло не так") // TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Files.createDirectories(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO
|
||||||
|
val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant())
|
||||||
|
writeMeta(pair.metaFile, newMeta)
|
||||||
|
return LocalDirectory(newMeta, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun touchFile(path: String): Unit = withContext(ioDispatcher) {
|
||||||
|
createFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun touchDir(path: String): Unit = withContext(ioDispatcher) {
|
||||||
|
createDir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(path: String) = withContext(ioDispatcher) {
|
override suspend fun delete(path: String) = withContext(ioDispatcher) {
|
||||||
TODO("Not yet implemented")
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||||
|
if(pair != null) {
|
||||||
|
pair.file.delete()
|
||||||
|
pair.metaFile.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getFileInfo(path: String) = withContext(ioDispatcher) {
|
override suspend fun openWrite(path: String): OutputStream = withContext(ioDispatcher) {
|
||||||
TODO("Not yet implemented")
|
touchFile(path)
|
||||||
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO
|
||||||
|
return@withContext pair.file.outputStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDirInfo(path: String) = withContext(ioDispatcher) {
|
override suspend fun openRead(path: String): InputStream = withContext(ioDispatcher) {
|
||||||
TODO("Not yet implemented")
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO
|
||||||
}
|
return@withContext pair.file.inputStream()
|
||||||
|
|
||||||
override suspend fun openWrite(path: String): InputStream = withContext(ioDispatcher) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun openRead(path: String): OutputStream = withContext(ioDispatcher) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun moveToTrash(path: String) = withContext(ioDispatcher) {
|
override suspend fun moveToTrash(path: String) = withContext(ioDispatcher) {
|
||||||
TODO("Not yet implemented")
|
val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO
|
||||||
|
val newMeta = pair.meta.copy(isDeleted = true)
|
||||||
|
writeMeta(pair.metaFile, newMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package com.github.nullptroma.wallenc.domain.datatypes
|
package com.github.nullptroma.wallenc.domain.datatypes
|
||||||
|
|
||||||
class EncryptKey {
|
class EncryptKey(val key: String) {
|
||||||
val key: String
|
|
||||||
|
|
||||||
constructor(key: String) {
|
|
||||||
this@EncryptKey.key = key
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -35,9 +35,7 @@ interface IStorageAccessor {
|
|||||||
suspend fun touchFile(path: String)
|
suspend fun touchFile(path: String)
|
||||||
suspend fun touchDir(path: String)
|
suspend fun touchDir(path: String)
|
||||||
suspend fun delete(path: String)
|
suspend fun delete(path: String)
|
||||||
suspend fun getFileInfo(path: String)
|
suspend fun openWrite(path: String): OutputStream
|
||||||
suspend fun getDirInfo(path: String)
|
suspend fun openRead(path: String): InputStream
|
||||||
suspend fun openWrite(path: String): InputStream
|
|
||||||
suspend fun openRead(path: String): OutputStream
|
|
||||||
suspend fun moveToTrash(path: String)
|
suspend fun moveToTrash(path: String)
|
||||||
}
|
}
|
||||||
@@ -1209,8 +1209,6 @@ existing classes or even new classes with specific responsibilities.</val>
|
|||||||
<ref refid="65a4baa8-9613-11ef-86f7-830924f9008e"/>
|
<ref refid="65a4baa8-9613-11ef-86f7-830924f9008e"/>
|
||||||
<ref refid="6e3f7c1c-9613-11ef-98ca-830924f9008e"/>
|
<ref refid="6e3f7c1c-9613-11ef-98ca-830924f9008e"/>
|
||||||
<ref refid="88974282-9613-11ef-bbae-830924f9008e"/>
|
<ref refid="88974282-9613-11ef-bbae-830924f9008e"/>
|
||||||
<ref refid="9c27a6ba-9613-11ef-aae0-830924f9008e"/>
|
|
||||||
<ref refid="a1007ad1-9613-11ef-bab0-830924f9008e"/>
|
|
||||||
<ref refid="a9e08b31-9613-11ef-a5b0-830924f9008e"/>
|
<ref refid="a9e08b31-9613-11ef-a5b0-830924f9008e"/>
|
||||||
<ref refid="b2c24289-9613-11ef-9d22-830924f9008e"/>
|
<ref refid="b2c24289-9613-11ef-9d22-830924f9008e"/>
|
||||||
<ref refid="bd882d77-9613-11ef-9b99-830924f9008e"/>
|
<ref refid="bd882d77-9613-11ef-9b99-830924f9008e"/>
|
||||||
@@ -1409,60 +1407,6 @@ existing classes or even new classes with specific responsibilities.</val>
|
|||||||
<val>Uri</val>
|
<val>Uri</val>
|
||||||
</typeValue>
|
</typeValue>
|
||||||
</Parameter>
|
</Parameter>
|
||||||
<Operation id="9c27a6ba-9613-11ef-aae0-830924f9008e">
|
|
||||||
<interface_>
|
|
||||||
<ref refid="44426384-949b-11ef-8612-9b5d5b59175b"/>
|
|
||||||
</interface_>
|
|
||||||
<name>
|
|
||||||
<val>getFileInfo</val>
|
|
||||||
</name>
|
|
||||||
<ownedParameter>
|
|
||||||
<reflist>
|
|
||||||
<ref refid="9c2a08b8-9613-11ef-98c6-830924f9008e"/>
|
|
||||||
</reflist>
|
|
||||||
</ownedParameter>
|
|
||||||
</Operation>
|
|
||||||
<Parameter id="9c2a08b8-9613-11ef-98c6-830924f9008e">
|
|
||||||
<direction>
|
|
||||||
<val>in</val>
|
|
||||||
</direction>
|
|
||||||
<name>
|
|
||||||
<val>path</val>
|
|
||||||
</name>
|
|
||||||
<ownerFormalParam>
|
|
||||||
<ref refid="9c27a6ba-9613-11ef-aae0-830924f9008e"/>
|
|
||||||
</ownerFormalParam>
|
|
||||||
<typeValue>
|
|
||||||
<val>Uri</val>
|
|
||||||
</typeValue>
|
|
||||||
</Parameter>
|
|
||||||
<Operation id="a1007ad1-9613-11ef-bab0-830924f9008e">
|
|
||||||
<interface_>
|
|
||||||
<ref refid="44426384-949b-11ef-8612-9b5d5b59175b"/>
|
|
||||||
</interface_>
|
|
||||||
<name>
|
|
||||||
<val>getDirInfo</val>
|
|
||||||
</name>
|
|
||||||
<ownedParameter>
|
|
||||||
<reflist>
|
|
||||||
<ref refid="a103d601-9613-11ef-8f30-830924f9008e"/>
|
|
||||||
</reflist>
|
|
||||||
</ownedParameter>
|
|
||||||
</Operation>
|
|
||||||
<Parameter id="a103d601-9613-11ef-8f30-830924f9008e">
|
|
||||||
<direction>
|
|
||||||
<val>in</val>
|
|
||||||
</direction>
|
|
||||||
<name>
|
|
||||||
<val>path</val>
|
|
||||||
</name>
|
|
||||||
<ownerFormalParam>
|
|
||||||
<ref refid="a1007ad1-9613-11ef-bab0-830924f9008e"/>
|
|
||||||
</ownerFormalParam>
|
|
||||||
<typeValue>
|
|
||||||
<val>Uri</val>
|
|
||||||
</typeValue>
|
|
||||||
</Parameter>
|
|
||||||
<Operation id="a9e08b31-9613-11ef-a5b0-830924f9008e">
|
<Operation id="a9e08b31-9613-11ef-a5b0-830924f9008e">
|
||||||
<interface_>
|
<interface_>
|
||||||
<ref refid="44426384-949b-11ef-8612-9b5d5b59175b"/>
|
<ref refid="44426384-949b-11ef-8612-9b5d5b59175b"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user