file_source.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package types
  2. import (
  3. "fmt"
  4. "image"
  5. "os"
  6. "strings"
  7. "sync"
  8. )
  9. // FileSource 统一的文件来源抽象接口
  10. // 支持 URL 和 base64 两种来源,提供懒加载和缓存机制
  11. type FileSource interface {
  12. IsURL() bool
  13. GetIdentifier() string
  14. GetRawData() string
  15. ClearRawData()
  16. SetCache(data *CachedFileData)
  17. GetCache() *CachedFileData
  18. HasCache() bool
  19. ClearCache()
  20. IsRegistered() bool
  21. SetRegistered(registered bool)
  22. Mu() *sync.Mutex
  23. }
  24. // baseFileSource 共享的缓存/锁/清理注册状态
  25. type baseFileSource struct {
  26. cachedData *CachedFileData
  27. cacheLoaded bool
  28. registered bool
  29. mu sync.Mutex
  30. }
  31. func (b *baseFileSource) SetCache(data *CachedFileData) {
  32. b.cachedData = data
  33. b.cacheLoaded = true
  34. }
  35. func (b *baseFileSource) GetCache() *CachedFileData {
  36. return b.cachedData
  37. }
  38. func (b *baseFileSource) HasCache() bool {
  39. return b.cacheLoaded && b.cachedData != nil
  40. }
  41. func (b *baseFileSource) ClearCache() {
  42. if b.cachedData != nil {
  43. b.cachedData.Close()
  44. }
  45. b.cachedData = nil
  46. b.cacheLoaded = false
  47. }
  48. func (b *baseFileSource) IsRegistered() bool {
  49. return b.registered
  50. }
  51. func (b *baseFileSource) SetRegistered(registered bool) {
  52. b.registered = registered
  53. }
  54. func (b *baseFileSource) Mu() *sync.Mutex {
  55. return &b.mu
  56. }
  57. // ---------------------------------------------------------------------------
  58. // URLSource — URL 来源的 FileSource 实现
  59. // ---------------------------------------------------------------------------
  60. type URLSource struct {
  61. baseFileSource
  62. URL string
  63. }
  64. func (u *URLSource) IsURL() bool { return true }
  65. func (u *URLSource) GetIdentifier() string {
  66. if len(u.URL) > 100 {
  67. return u.URL[:100] + "..."
  68. }
  69. return u.URL
  70. }
  71. func (u *URLSource) GetRawData() string { return u.URL }
  72. func (u *URLSource) ClearRawData() {}
  73. // ---------------------------------------------------------------------------
  74. // Base64Source — Base64 内联数据来源的 FileSource 实现
  75. // ---------------------------------------------------------------------------
  76. type Base64Source struct {
  77. baseFileSource
  78. Base64Data string
  79. MimeType string
  80. }
  81. func (b *Base64Source) IsURL() bool { return false }
  82. func (b *Base64Source) GetIdentifier() string {
  83. if len(b.Base64Data) > 50 {
  84. return "base64:" + b.Base64Data[:50] + "..."
  85. }
  86. return "base64:" + b.Base64Data
  87. }
  88. func (b *Base64Source) GetRawData() string { return b.Base64Data }
  89. func (b *Base64Source) ClearRawData() {
  90. if len(b.Base64Data) > 1024 {
  91. b.Base64Data = ""
  92. }
  93. }
  94. // ---------------------------------------------------------------------------
  95. // Constructors
  96. // ---------------------------------------------------------------------------
  97. func NewURLFileSource(url string) *URLSource {
  98. return &URLSource{URL: url}
  99. }
  100. func NewBase64FileSource(base64Data string, mimeType string) *Base64Source {
  101. return &Base64Source{
  102. Base64Data: base64Data,
  103. MimeType: mimeType,
  104. }
  105. }
  106. func NewFileSourceFromData(data string, mimeType string) FileSource {
  107. if strings.HasPrefix(data, "http://") || strings.HasPrefix(data, "https://") {
  108. return NewURLFileSource(data)
  109. }
  110. return NewBase64FileSource(data, mimeType)
  111. }
  112. // ---------------------------------------------------------------------------
  113. // CachedFileData — 缓存的文件数据(支持内存和磁盘两种模式)
  114. // ---------------------------------------------------------------------------
  115. type CachedFileData struct {
  116. base64Data string // 内存中的 base64 数据(小文件)
  117. MimeType string // MIME 类型
  118. Size int64 // 文件大小(字节)
  119. DiskSize int64 // 磁盘缓存实际占用大小(字节,通常是 base64 长度)
  120. ImageConfig *image.Config // 图片配置(如果是图片)
  121. ImageFormat string // 图片格式(如果是图片)
  122. diskPath string // 磁盘缓存文件路径(大文件)
  123. isDisk bool // 是否使用磁盘缓存
  124. diskMu sync.Mutex // 磁盘操作锁(保护磁盘文件的读取和删除)
  125. diskClosed bool // 是否已关闭/清理
  126. statDecremented bool // 是否已扣减统计
  127. OnClose func(size int64)
  128. }
  129. func NewMemoryCachedData(base64Data string, mimeType string, size int64) *CachedFileData {
  130. return &CachedFileData{
  131. base64Data: base64Data,
  132. MimeType: mimeType,
  133. Size: size,
  134. isDisk: false,
  135. }
  136. }
  137. func NewDiskCachedData(diskPath string, mimeType string, size int64) *CachedFileData {
  138. return &CachedFileData{
  139. diskPath: diskPath,
  140. MimeType: mimeType,
  141. Size: size,
  142. isDisk: true,
  143. }
  144. }
  145. func (c *CachedFileData) GetBase64Data() (string, error) {
  146. if !c.isDisk {
  147. return c.base64Data, nil
  148. }
  149. c.diskMu.Lock()
  150. defer c.diskMu.Unlock()
  151. if c.diskClosed {
  152. return "", fmt.Errorf("disk cache already closed")
  153. }
  154. data, err := os.ReadFile(c.diskPath)
  155. if err != nil {
  156. return "", fmt.Errorf("failed to read from disk cache: %w", err)
  157. }
  158. return string(data), nil
  159. }
  160. func (c *CachedFileData) SetBase64Data(data string) {
  161. if !c.isDisk {
  162. c.base64Data = data
  163. }
  164. }
  165. func (c *CachedFileData) IsDisk() bool {
  166. return c.isDisk
  167. }
  168. func (c *CachedFileData) Close() error {
  169. if !c.isDisk {
  170. c.base64Data = ""
  171. return nil
  172. }
  173. c.diskMu.Lock()
  174. defer c.diskMu.Unlock()
  175. if c.diskClosed {
  176. return nil
  177. }
  178. c.diskClosed = true
  179. if c.diskPath != "" {
  180. err := os.Remove(c.diskPath)
  181. if err == nil && !c.statDecremented && c.OnClose != nil {
  182. c.OnClose(c.DiskSize)
  183. c.statDecremented = true
  184. }
  185. return err
  186. }
  187. return nil
  188. }