pull/2508/head
Horis 2 years ago
parent f01195470e
commit 441f696294
  1. 16
      app/src/main/java/io/legado/app/help/AppWebDav.kt
  2. 22
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  3. 31
      app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt
  4. 23
      app/src/main/java/io/legado/app/utils/UriExtensions.kt

@ -1,6 +1,7 @@
package io.legado.app.help package io.legado.app.help
import android.content.Context import android.content.Context
import android.net.Uri
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
@ -207,6 +208,21 @@ object AppWebDav {
} }
} }
suspend fun exportWebDav(uri: Uri, fileName: String) {
if (!NetworkUtils.isAvailable()) return
try {
authorization?.let {
// 如果导出的本地文件存在,开始上传
val putUrl = exportsWebDavUrl + fileName
WebDav(putUrl, it).upload(uri, "text/plain")
}
} catch (e: Exception) {
val msg = "WebDav导出\n${e.localizedMessage}"
AppLog.put(msg, e)
appCtx.toastOnUi(msg)
}
}
fun uploadBookProgress(book: Book) { fun uploadBookProgress(book: Book) {
val authorization = authorization ?: return val authorization = authorization ?: return
if (!syncBookProgress) return if (!syncBookProgress) return

@ -1,6 +1,7 @@
package io.legado.app.lib.webdav package io.legado.app.lib.webdav
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.http.newCallResponse import io.legado.app.help.http.newCallResponse
@ -8,6 +9,7 @@ import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text import io.legado.app.help.http.text
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.printOnDebug import io.legado.app.utils.printOnDebug
import io.legado.app.utils.toRequestBody
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.Interceptor import okhttp3.Interceptor
@ -316,6 +318,26 @@ open class WebDav(val path: String, val authorization: Authorization) {
} }
} }
@Throws(WebDavException::class)
suspend fun upload(uri: Uri, contentType: String) {
// 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息
kotlin.runCatching {
withContext(IO) {
val fileBody = uri.toRequestBody(contentType.toMediaType())
val url = httpUrl ?: throw NoStackTraceException("url不能为空")
webDavClient.newCallResponse {
url(url)
put(fileBody)
}.let {
checkResult(it)
}
}
}.onFailure {
AppLog.put("WebDav上传失败\n${it.localizedMessage}", it)
throw WebDavException("WebDav上传失败\n${it.localizedMessage}")
}
}
@Throws(WebDavException::class) @Throws(WebDavException::class)
suspend fun downloadInputStream(): InputStream { suspend fun downloadInputStream(): InputStream {
val url = httpUrl ?: throw WebDavException("WebDav下载出错\nurl为空") val url = httpUrl ?: throw WebDavException("WebDav下载出错\nurl为空")

@ -122,9 +122,11 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
exportMsg[book.bookUrl] = context.getString(R.string.export_success) exportMsg[book.bookUrl] = context.getString(R.string.export_success)
upAdapterLiveData.postValue(book.bookUrl) upAdapterLiveData.postValue(book.bookUrl)
}.onFinally { }.onFinally {
mutex.withLock {
exportNumber-- exportNumber--
} }
} }
}
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
private suspend fun export(doc: DocumentFile, book: Book) { private suspend fun export(doc: DocumentFile, book: Book) {
@ -132,14 +134,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
DocumentUtils.delete(doc, filename) DocumentUtils.delete(doc, filename)
val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename) val bookDoc = DocumentUtils.createFileIfNotExist(doc, filename)
?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹") ?: throw NoStackTraceException("创建文档失败,请尝试重新设置导出文件夹")
val stringBuilder = StringBuilder()
val exportToWebDav = AppConfig.exportToWebDav
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
getAllContents(book) { text, srcList -> getAllContents(book) { text, srcList ->
bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset))) bookOs.write(text.toByteArray(Charset.forName(AppConfig.exportCharset)))
if (exportToWebDav) {
stringBuilder.append(text)
}
srcList?.forEach { srcList?.forEach {
val vFile = BookHelp.getImage(book, it.third) val vFile = BookHelp.getImage(book, it.third)
if (vFile.exists()) { if (vFile.exists()) {
@ -154,9 +151,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
} }
if (AppConfig.exportToWebDav) { if (AppConfig.exportToWebDav) {
// 导出到webdav // 导出到webdav
val byteArray = AppWebDav.exportWebDav(bookDoc.uri, filename)
stringBuilder.toString().toByteArray(Charset.forName(AppConfig.exportCharset))
AppWebDav.exportWebDav(byteArray, filename)
} }
} }
@ -164,13 +159,8 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val filename = "${getExportFileName(book)}.txt" val filename = "${getExportFileName(book)}.txt"
val bookPath = FileUtils.getPath(file, filename) val bookPath = FileUtils.getPath(file, filename)
val bookFile = FileUtils.createFileWithReplace(bookPath) val bookFile = FileUtils.createFileWithReplace(bookPath)
val stringBuilder = StringBuilder()
val exportToWebDav = AppConfig.exportToWebDav
getAllContents(book) { text, srcList -> getAllContents(book) { text, srcList ->
bookFile.appendText(text, Charset.forName(AppConfig.exportCharset)) bookFile.appendText(text, Charset.forName(AppConfig.exportCharset))
if (exportToWebDav) {
stringBuilder.append(text)
}
srcList?.forEach { srcList?.forEach {
val vFile = BookHelp.getImage(book, it.third) val vFile = BookHelp.getImage(book, it.third)
if (vFile.exists()) { if (vFile.exists()) {
@ -185,9 +175,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
} }
} }
if (AppConfig.exportToWebDav) { if (AppConfig.exportToWebDav) {
val byteArray = AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename) // 导出到webdav
stringBuilder.toString().toByteArray(Charset.forName(AppConfig.exportCharset))
AppWebDav.exportWebDav(byteArray, filename) // 导出到webdav
} }
} }
@ -299,9 +287,11 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
exportMsg[book.bookUrl] = context.getString(R.string.export_success) exportMsg[book.bookUrl] = context.getString(R.string.export_success)
upAdapterLiveData.postValue(book.bookUrl) upAdapterLiveData.postValue(book.bookUrl)
}.onFinally { }.onFinally {
mutex.withLock {
exportNumber-- exportNumber--
} }
} }
}
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
private suspend fun exportEpub(doc: DocumentFile, book: Book) { private suspend fun exportEpub(doc: DocumentFile, book: Book) {
@ -322,7 +312,10 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs ->
EpubWriter().write(epubBook, bookOs) EpubWriter().write(epubBook, bookOs)
} }
if (AppConfig.exportToWebDav) {
// 导出到webdav
AppWebDav.exportWebDav(bookDoc.uri, filename)
}
} }
} }
@ -344,6 +337,10 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
setEpubContent(contentModel, book, epubBook) setEpubContent(contentModel, book, epubBook)
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
EpubWriter().write(epubBook, FileOutputStream(bookFile)) EpubWriter().write(epubBook, FileOutputStream(bookFile))
if (AppConfig.exportToWebDav) {
// 导出到webdav
AppWebDav.exportWebDav(Uri.fromFile(bookFile), filename)
}
} }
private fun setAssets(doc: DocumentFile, book: Book, epubBook: EpubBook): String { private fun setAssets(doc: DocumentFile, book: Book, epubBook: EpubBook): String {

@ -10,6 +10,11 @@ import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.lib.permission.Permissions import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat import io.legado.app.lib.permission.PermissionsCompat
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import okio.source
import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStream import java.io.InputStream
@ -198,3 +203,21 @@ fun Uri.inputStream(context: Context): Result<InputStream> {
} }
} }
} }
fun Uri.toRequestBody(contentType: MediaType? = null): RequestBody {
val uri = this
return object : RequestBody() {
override fun contentType() = contentType
override fun contentLength(): Long {
val length = uri.inputStream(appCtx).getOrThrow().available().toLong()
return if (length > 0) length else -1
}
override fun writeTo(sink: BufferedSink) {
uri.inputStream(appCtx).getOrThrow().source().use { source ->
sink.writeAll(source)
}
}
}
}
Loading…
Cancel
Save