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. 35
      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
import android.content.Context
import android.net.Uri
import io.legado.app.R
import io.legado.app.constant.AppLog
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) {
val authorization = authorization ?: return
if (!syncBookProgress) return

@ -1,6 +1,7 @@
package io.legado.app.lib.webdav
import android.annotation.SuppressLint
import android.net.Uri
import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException
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.utils.NetworkUtils
import io.legado.app.utils.printOnDebug
import io.legado.app.utils.toRequestBody
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
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)
suspend fun downloadInputStream(): InputStream {
val url = httpUrl ?: throw WebDavException("WebDav下载出错\nurl为空")

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

@ -10,6 +10,11 @@ import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException
import io.legado.app.lib.permission.Permissions
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.FileInputStream
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