FileCacheStrategy.kt 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /*
  2. * Copyright 2019-2023 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:Suppress("unused", "MemberVisibilityCanBePrivate")
  10. package net.mamoe.mirai.utils
  11. import io.ktor.utils.io.errors.*
  12. import kotlinx.coroutines.Dispatchers
  13. import net.mamoe.mirai.Bot
  14. import net.mamoe.mirai.IMirai
  15. import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo
  16. import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
  17. import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage
  18. import net.mamoe.mirai.utils.FileCacheStrategy.MemoryCache
  19. import net.mamoe.mirai.utils.FileCacheStrategy.TempCache
  20. import java.io.File
  21. import java.io.InputStream
  22. /**
  23. * 资源缓存策略.
  24. *
  25. * 由于上传资源时服务器要求提前给出 MD5 和文件大小等数据, 一些资源如 [InputStream] 需要首先缓存才能使用.
  26. *
  27. * 资源的缓存都是将 [InputStream] 缓存未 [ExternalResource]. 根据 [FileCacheStrategy] 实现不同, 可以以临时文件存储, 也可以在数据库或是内存按需存储.
  28. * Mirai 内置的实现有 [内存存储][MemoryCache] 和 [临时文件存储][TempCache].
  29. * 操作 [ExternalResource.toExternalResource] 时将会使用 [IMirai.FileCacheStrategy]. 可以覆盖, 示例:
  30. * ```
  31. * // Kotlin
  32. * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache() // 使用系统默认缓存路径, 也是默认的行为
  33. * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache(File("C:/cache")) // 使用自定义缓存路径
  34. *
  35. * // Java
  36. * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache()); // 使用系统默认缓存路径, 也是默认的行为
  37. * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache(new File("C:/cache"))); // 使用自定义的缓存路径
  38. * ```
  39. *
  40. * 此接口的实现和使用都是稳定的. 自行实现的 [FileCacheStrategy] 也可以被 Mirai 使用.
  41. *
  42. * 注意, 此接口目前仅缓存 [InputStream] 等一次性数据. 好友列表等数据由每个 [Bot] 的 [BotConfiguration.cacheDir] 缓存.
  43. *
  44. * ### 使用 [FileCacheStrategy] 的操作
  45. * - [ExternalResource.toExternalResource]
  46. * - [ExternalResource.uploadAsImage]
  47. * - [ExternalResource.sendAsImageTo]
  48. *
  49. * @see ExternalResource
  50. */
  51. public interface FileCacheStrategy {
  52. /**
  53. * 立即读取 [input] 所有内容并缓存为 [ExternalResource].
  54. *
  55. * 注意:
  56. * - 此函数不会关闭输入
  57. * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]).
  58. *
  59. * @param formatName 文件类型. 此参数通常只会影响官方客户端接收到的文件的文件后缀. 若为 `null` 则会自动根据文件头识别. 识别失败时将使用 "mirai"
  60. */
  61. @Throws(IOException::class)
  62. public fun newCache(input: InputStream, formatName: String? = null): ExternalResource
  63. /**
  64. * 立即读取 [input] 所有内容并缓存为 [ExternalResource]. 自动根据文件头识别文件类型. 识别失败时将使用 "mirai".
  65. *
  66. * 注意:
  67. * - 此函数不会关闭输入
  68. * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]).
  69. */
  70. @Throws(IOException::class)
  71. public fun newCache(input: InputStream): ExternalResource = newCache(input, null)
  72. /**
  73. * 使用内存直接存储所有图片文件. 由 JVM 执行 GC.
  74. */
  75. public object MemoryCache : FileCacheStrategy {
  76. @Throws(IOException::class)
  77. override fun newCache(input: InputStream, formatName: String?): ExternalResource {
  78. return input.readBytes().toExternalResource(formatName)
  79. }
  80. }
  81. /**
  82. * 使用系统临时文件夹缓存图片文件. 在图片使用完毕后或 JVM 正常结束时删除临时文件.
  83. */
  84. public class TempCache @JvmOverloads public constructor(
  85. /**
  86. * 缓存图片存放位置. 为 `null` 时使用主机系统的临时文件夹: `File.createTempFile("tmp", null, directory)`
  87. */
  88. public val directory: File? = null,
  89. ) : FileCacheStrategy {
  90. private fun createTempFile(): File {
  91. return File.createTempFile("tmp", null, directory)
  92. }
  93. @Throws(IOException::class)
  94. override fun newCache(input: InputStream, formatName: String?): ExternalResource {
  95. val file = createTempFile()
  96. return file.apply {
  97. deleteOnExit()
  98. outputStream().use { out -> input.copyTo(out) }
  99. }.toExternalResource(formatName).apply {
  100. closed.invokeOnCompletion {
  101. kotlin.runCatching { file.delete() }
  102. }
  103. }
  104. }
  105. }
  106. public companion object {
  107. /**
  108. * 当前平台下默认的缓存策略. 注意, 这可能不是 Mirai 全局默认使用的, Mirai 从 [IMirai.FileCacheStrategy] 获取.
  109. *
  110. * @see IMirai.FileCacheStrategy
  111. */
  112. @MiraiExperimentalApi
  113. @JvmStatic
  114. public val PlatformDefault: FileCacheStrategy = TempCache(null)
  115. }
  116. }