disk_cache.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package common
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "time"
  7. "github.com/google/uuid"
  8. )
  9. // DiskCacheType 磁盘缓存类型
  10. type DiskCacheType string
  11. const (
  12. DiskCacheTypeBody DiskCacheType = "body" // 请求体缓存
  13. DiskCacheTypeFile DiskCacheType = "file" // 文件数据缓存
  14. )
  15. // 统一的缓存目录名
  16. const diskCacheDir = "new-api-body-cache"
  17. // GetDiskCacheDir 获取统一的磁盘缓存目录
  18. // 注意:每次调用都会重新计算,以响应配置变化
  19. func GetDiskCacheDir() string {
  20. cachePath := GetDiskCachePath()
  21. if cachePath == "" {
  22. cachePath = os.TempDir()
  23. }
  24. return filepath.Join(cachePath, diskCacheDir)
  25. }
  26. // EnsureDiskCacheDir 确保缓存目录存在
  27. func EnsureDiskCacheDir() error {
  28. dir := GetDiskCacheDir()
  29. return os.MkdirAll(dir, 0755)
  30. }
  31. // CreateDiskCacheFile 创建磁盘缓存文件
  32. // cacheType: 缓存类型(body/file)
  33. // 返回文件路径和文件句柄
  34. func CreateDiskCacheFile(cacheType DiskCacheType) (string, *os.File, error) {
  35. if err := EnsureDiskCacheDir(); err != nil {
  36. return "", nil, fmt.Errorf("failed to create cache directory: %w", err)
  37. }
  38. dir := GetDiskCacheDir()
  39. filename := fmt.Sprintf("%s-%s-%d.tmp", cacheType, uuid.New().String()[:8], time.Now().UnixNano())
  40. filePath := filepath.Join(dir, filename)
  41. file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_EXCL, 0600)
  42. if err != nil {
  43. return "", nil, fmt.Errorf("failed to create cache file: %w", err)
  44. }
  45. return filePath, file, nil
  46. }
  47. // WriteDiskCacheFile 写入数据到磁盘缓存文件
  48. // 返回文件路径
  49. func WriteDiskCacheFile(cacheType DiskCacheType, data []byte) (string, error) {
  50. filePath, file, err := CreateDiskCacheFile(cacheType)
  51. if err != nil {
  52. return "", err
  53. }
  54. _, err = file.Write(data)
  55. if err != nil {
  56. file.Close()
  57. os.Remove(filePath)
  58. return "", fmt.Errorf("failed to write cache file: %w", err)
  59. }
  60. if err := file.Close(); err != nil {
  61. os.Remove(filePath)
  62. return "", fmt.Errorf("failed to close cache file: %w", err)
  63. }
  64. return filePath, nil
  65. }
  66. // WriteDiskCacheFileString 写入字符串到磁盘缓存文件
  67. func WriteDiskCacheFileString(cacheType DiskCacheType, data string) (string, error) {
  68. return WriteDiskCacheFile(cacheType, []byte(data))
  69. }
  70. // ReadDiskCacheFile 读取磁盘缓存文件
  71. func ReadDiskCacheFile(filePath string) ([]byte, error) {
  72. return os.ReadFile(filePath)
  73. }
  74. // ReadDiskCacheFileString 读取磁盘缓存文件为字符串
  75. func ReadDiskCacheFileString(filePath string) (string, error) {
  76. data, err := os.ReadFile(filePath)
  77. if err != nil {
  78. return "", err
  79. }
  80. return string(data), nil
  81. }
  82. // RemoveDiskCacheFile 删除磁盘缓存文件
  83. func RemoveDiskCacheFile(filePath string) error {
  84. return os.Remove(filePath)
  85. }
  86. // CleanupOldDiskCacheFiles 清理旧的缓存文件
  87. // maxAge: 文件最大存活时间
  88. // 注意:此函数只删除文件,不更新统计(因为无法知道每个文件的原始大小)
  89. func CleanupOldDiskCacheFiles(maxAge time.Duration) error {
  90. dir := GetDiskCacheDir()
  91. entries, err := os.ReadDir(dir)
  92. if err != nil {
  93. if os.IsNotExist(err) {
  94. return nil // 目录不存在,无需清理
  95. }
  96. return err
  97. }
  98. now := time.Now()
  99. for _, entry := range entries {
  100. if entry.IsDir() {
  101. continue
  102. }
  103. info, err := entry.Info()
  104. if err != nil {
  105. continue
  106. }
  107. if now.Sub(info.ModTime()) > maxAge {
  108. // 注意:后台清理任务删除文件时,由于无法得知原始 base64Size,
  109. // 只能按磁盘文件大小扣减。这在目前 base64 存储模式下是准确的。
  110. if err := os.Remove(filepath.Join(dir, entry.Name())); err == nil {
  111. DecrementDiskFiles(info.Size())
  112. }
  113. }
  114. }
  115. return nil
  116. }
  117. // GetDiskCacheInfo 获取磁盘缓存目录信息
  118. func GetDiskCacheInfo() (fileCount int, totalSize int64, err error) {
  119. dir := GetDiskCacheDir()
  120. entries, err := os.ReadDir(dir)
  121. if err != nil {
  122. if os.IsNotExist(err) {
  123. return 0, 0, nil
  124. }
  125. return 0, 0, err
  126. }
  127. for _, entry := range entries {
  128. if entry.IsDir() {
  129. continue
  130. }
  131. info, err := entry.Info()
  132. if err != nil {
  133. continue
  134. }
  135. fileCount++
  136. totalSize += info.Size()
  137. }
  138. return fileCount, totalSize, nil
  139. }
  140. // ShouldUseDiskCache 判断是否应该使用磁盘缓存
  141. func ShouldUseDiskCache(dataSize int64) bool {
  142. if !IsDiskCacheEnabled() {
  143. return false
  144. }
  145. threshold := GetDiskCacheThresholdBytes()
  146. if dataSize < threshold {
  147. return false
  148. }
  149. return IsDiskCacheAvailable(dataSize)
  150. }