RemoteFiles.kt 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright 2019-2022 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/dev/LICENSE
  8. */
  9. @file:JvmBlockingBridge
  10. @file:Suppress("OVERLOADS_INTERFACE")
  11. package net.mamoe.mirai.contact.file
  12. import kotlinx.coroutines.flow.Flow
  13. import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
  14. import net.mamoe.mirai.contact.FileSupported
  15. import net.mamoe.mirai.contact.PermissionDeniedException
  16. import net.mamoe.mirai.utils.ExternalResource
  17. import net.mamoe.mirai.utils.NotStableForInheritance
  18. import net.mamoe.mirai.utils.ProgressionCallback
  19. import kotlin.jvm.JvmOverloads
  20. /**
  21. * 表示远程文件列表 (管理器).
  22. *
  23. * [RemoteFiles] 包含一些协议接口,
  24. *
  25. * # 文件和目录操作
  26. *
  27. * 文件和目录的父类型是 [AbsoluteFileFolder].
  28. *
  29. * - [AbsoluteFile] 表示一个文件
  30. * - [AbsoluteFolder] 表示一个目录
  31. *
  32. * 每个文件或目录都拥有一个唯一 ID: [AbsoluteFileFolder.id]. 该 ID 由服务器提供, 在重命名或移动时不会变化.
  33. *
  34. * 文件名可以通过 [AbsoluteFileFolder.name] 获得, 但注意文件名和其他属性都会随重命名或移动等操作更新.
  35. *
  36. * 除根目录 [root] 外, 每个文件或目录都拥有父目录 [AbsoluteFileFolder.parent].
  37. *
  38. * # 根目录
  39. *
  40. * 除了 [RemoteFiles] 中定义的捷径外, 一切文件目录操作都以获取根目录开始. 可通过 [RemoteFiles.root] 获取表示根目录的 [AbsoluteFolder].
  41. *
  42. * # 绝对路径与相对路径
  43. *
  44. * mirai 文件系统的绝对路径与相对路径与 Java [java.io.File] 实现的相同.
  45. *
  46. * 以 `/` 起始的路径表示绝对路径, 基于根目录 [root] 处理. 其他路径均表示相对路径.
  47. *
  48. * 可由 [AbsoluteFileFolder.absolutePath] 获取其绝对路径. 值得注意的是, 所有文件与目录对象都表示绝对路径下的目标, 因此它们都总是精确地表示一个目标, 而不受环境影响.
  49. *
  50. * 除重命名外, 所有文件和目录操作都默认同时支持上述两种路径.
  51. *
  52. * # 操作 [AbsoluteFileFolder]
  53. *
  54. * ## 重命名, 移动
  55. *
  56. * [AbsoluteFileFolder.renameTo], [AbsoluteFile.moveTo] 提供重命名和移动功能. 注意目录不支持移动.
  57. *
  58. * ## 获取目录中的子目录和文件列表
  59. *
  60. * 一个目录 ([AbsoluteFolder]) 可以包含多个子文件, 根目录还可以包含多个子目录 (详见下文 '目录结构限制').
  61. *
  62. * 使用 [AbsoluteFolder.children] 可以获得其内子目录和文件列表 [Flow]. [AbsoluteFolder.childrenStream] 提供适合 Java 的 [java.util.stream.Stream] 实现.
  63. * 使用 [AbsoluteFolder.folders] 或 [AbsoluteFolder.files] 可以特定地只获取子目录或文件列表. 这些函数也有其 `*Stream` 实现.
  64. *
  65. * 若要根据确定的文件或目录名称获取其 [AbsoluteFileFolder] 实例, 可使用 [AbsoluteFolder.resolveFiles] 或 [AbsoluteFolder.resolveFiles].
  66. * 注意 [AbsoluteFolder.resolveFiles] 返回 [Flow] (其 Stream 版返回 [java.util.stream.Stream]), 因为服务器允许多个文件有相同名称. (详见下文 '允许重名').
  67. *
  68. * 若已知文件 [AbsoluteFile.id], 可通过 [AbsoluteFolder.resolveFileById] 获得该文件.
  69. *
  70. * ## 上传新文件
  71. * 可使用 [AbsoluteFolder.uploadNewFile] 上传新文件. 也可以通过 [RemoteFiles.uploadNewFile] 直接上传而跳过获取目录的步骤 (因为目录不允许同名).
  72. *
  73. * ## 覆盖一个旧文件
  74. * 服务器不允许覆盖文件. 只能通过 [AbsoluteFile.delete] 删除文件后再上传新文件. 注意新旧文件的 [AbsoluteFile.id] 会不同.
  75. *
  76. * # 操作权限
  77. * 操作一个目录时总是需要管理员权限. 若群设置 "允许任何人上传文件", 则上传文件和操作自己上传的文件时都不需要特殊权限. 注意, 操作他人的文件时总是需要管理员权限.
  78. *
  79. * # 服务器限制
  80. *
  81. * ## 目录结构限制
  82. *
  83. * 在 mirai 2.8.0 发布时, 服务器仅允许两层目录结构. 也就是说只允许根目录存在子目录, 子目录不能包含另一个子目录.
  84. *
  85. * 为了考虑将来服务器可能升级, mirai 没有做实现上的限制. mirai 所有操作都支持多层目录, 但进行这样的操作时将会得到服务器错误, 方法会抛出 [IllegalStateException].
  86. *
  87. * ## 允许重名
  88. *
  89. * 服务器允许同名目录和文件存在. 如下同名的三个文件与一个目录是允许的, 但它们的 [AbsoluteFileFolder.id] 都互不相同:
  90. * ```
  91. * foo
  92. * |- test (目录)
  93. * |- test (文件)
  94. * |- test (文件)
  95. * |- test (文件)
  96. * ```
  97. * 注意, 目录不允许同名.
  98. *
  99. * [AbsoluteFileFolder] 依据 [AbsoluteFileFolder.id] 定位文件, 而不是通过文件名. 因此 [AbsoluteFileFolder] 总是精确地代表一个文件或目录.
  100. *
  101. * @since 2.8
  102. * @see FileSupported
  103. */
  104. @NotStableForInheritance
  105. public interface RemoteFiles {
  106. /**
  107. * 获取表示根目录的 [AbsoluteFolder]
  108. */
  109. public val root: AbsoluteFolder
  110. /**
  111. * 该对象所属 [FileSupported]
  112. */
  113. public val contact: FileSupported
  114. /**
  115. * 上传一个文件到指定精确路径. 返回指代该远程文件的 [AbsoluteFile].
  116. *
  117. * 会在必要时尝试创建远程目录.
  118. *
  119. * 也可以使用 [AbsoluteFolder.uploadNewFile].
  120. *
  121. * @param filepath 文件路径, **包含目标文件名**. 如 `/foo/bar.txt`. 若是相对目录则基于 [根目录][root] 处理.
  122. * @param content 文件内容
  123. * @param callback 下载进度回调, 传递的 `progression` 是已下载字节数.
  124. *
  125. * @throws PermissionDeniedException 当无管理员权限时抛出 (若群仅允许管理员上传)
  126. */
  127. @JvmOverloads
  128. public suspend fun uploadNewFile(
  129. filepath: String,
  130. content: ExternalResource,
  131. callback: ProgressionCallback<AbsoluteFile, Long>? = null,
  132. ): AbsoluteFile = root.uploadNewFile(filepath, content, callback)
  133. }