file_source.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package types
  2. import (
  3. "fmt"
  4. "image"
  5. "os"
  6. "sync"
  7. )
  8. // FileSourceType 文件来源类型
  9. type FileSourceType string
  10. const (
  11. FileSourceTypeURL FileSourceType = "url" // URL 来源
  12. FileSourceTypeBase64 FileSourceType = "base64" // Base64 内联数据
  13. )
  14. // FileSource 统一的文件来源抽象
  15. // 支持 URL 和 base64 两种来源,提供懒加载和缓存机制
  16. type FileSource struct {
  17. Type FileSourceType `json:"type"` // 来源类型
  18. URL string `json:"url,omitempty"` // URL(当 Type 为 url 时)
  19. Base64Data string `json:"base64_data,omitempty"` // Base64 数据(当 Type 为 base64 时)
  20. MimeType string `json:"mime_type,omitempty"` // MIME 类型(可选,会自动检测)
  21. // 内部缓存(不导出,不序列化)
  22. cachedData *CachedFileData
  23. cacheLoaded bool
  24. registered bool // 是否已注册到清理列表
  25. mu sync.Mutex // 保护加载过程
  26. }
  27. // Mu 获取内部锁
  28. func (f *FileSource) Mu() *sync.Mutex {
  29. return &f.mu
  30. }
  31. // CachedFileData 缓存的文件数据
  32. // 支持内存缓存和磁盘缓存两种模式
  33. type CachedFileData struct {
  34. base64Data string // 内存中的 base64 数据(小文件)
  35. MimeType string // MIME 类型
  36. Size int64 // 文件大小(字节)
  37. DiskSize int64 // 磁盘缓存实际占用大小(字节,通常是 base64 长度)
  38. ImageConfig *image.Config // 图片配置(如果是图片)
  39. ImageFormat string // 图片格式(如果是图片)
  40. // 磁盘缓存相关
  41. diskPath string // 磁盘缓存文件路径(大文件)
  42. isDisk bool // 是否使用磁盘缓存
  43. diskMu sync.Mutex // 磁盘操作锁(保护磁盘文件的读取和删除)
  44. diskClosed bool // 是否已关闭/清理
  45. statDecremented bool // 是否已扣减统计
  46. // 统计回调,避免循环依赖
  47. OnClose func(size int64)
  48. }
  49. // NewMemoryCachedData 创建内存缓存的数据
  50. func NewMemoryCachedData(base64Data string, mimeType string, size int64) *CachedFileData {
  51. return &CachedFileData{
  52. base64Data: base64Data,
  53. MimeType: mimeType,
  54. Size: size,
  55. isDisk: false,
  56. }
  57. }
  58. // NewDiskCachedData 创建磁盘缓存的数据
  59. func NewDiskCachedData(diskPath string, mimeType string, size int64) *CachedFileData {
  60. return &CachedFileData{
  61. diskPath: diskPath,
  62. MimeType: mimeType,
  63. Size: size,
  64. isDisk: true,
  65. }
  66. }
  67. // GetBase64Data 获取 base64 数据(自动处理内存/磁盘)
  68. func (c *CachedFileData) GetBase64Data() (string, error) {
  69. if !c.isDisk {
  70. return c.base64Data, nil
  71. }
  72. c.diskMu.Lock()
  73. defer c.diskMu.Unlock()
  74. if c.diskClosed {
  75. return "", fmt.Errorf("disk cache already closed")
  76. }
  77. // 从磁盘读取
  78. data, err := os.ReadFile(c.diskPath)
  79. if err != nil {
  80. return "", fmt.Errorf("failed to read from disk cache: %w", err)
  81. }
  82. return string(data), nil
  83. }
  84. // SetBase64Data 设置 base64 数据(仅用于内存模式)
  85. func (c *CachedFileData) SetBase64Data(data string) {
  86. if !c.isDisk {
  87. c.base64Data = data
  88. }
  89. }
  90. // IsDisk 是否使用磁盘缓存
  91. func (c *CachedFileData) IsDisk() bool {
  92. return c.isDisk
  93. }
  94. // Close 关闭并清理资源
  95. func (c *CachedFileData) Close() error {
  96. if !c.isDisk {
  97. c.base64Data = "" // 释放内存
  98. return nil
  99. }
  100. c.diskMu.Lock()
  101. defer c.diskMu.Unlock()
  102. if c.diskClosed {
  103. return nil
  104. }
  105. c.diskClosed = true
  106. if c.diskPath != "" {
  107. err := os.Remove(c.diskPath)
  108. // 只有在删除成功且未扣减过统计时,才执行回调
  109. if err == nil && !c.statDecremented && c.OnClose != nil {
  110. c.OnClose(c.DiskSize)
  111. c.statDecremented = true
  112. }
  113. return err
  114. }
  115. return nil
  116. }
  117. // NewURLFileSource 创建 URL 来源的 FileSource
  118. func NewURLFileSource(url string) *FileSource {
  119. return &FileSource{
  120. Type: FileSourceTypeURL,
  121. URL: url,
  122. }
  123. }
  124. // NewBase64FileSource 创建 base64 来源的 FileSource
  125. func NewBase64FileSource(base64Data string, mimeType string) *FileSource {
  126. return &FileSource{
  127. Type: FileSourceTypeBase64,
  128. Base64Data: base64Data,
  129. MimeType: mimeType,
  130. }
  131. }
  132. // IsURL 判断是否是 URL 来源
  133. func (f *FileSource) IsURL() bool {
  134. return f.Type == FileSourceTypeURL
  135. }
  136. // IsBase64 判断是否是 base64 来源
  137. func (f *FileSource) IsBase64() bool {
  138. return f.Type == FileSourceTypeBase64
  139. }
  140. // GetIdentifier 获取文件标识符(用于日志和错误追踪)
  141. func (f *FileSource) GetIdentifier() string {
  142. if f.IsURL() {
  143. if len(f.URL) > 100 {
  144. return f.URL[:100] + "..."
  145. }
  146. return f.URL
  147. }
  148. if len(f.Base64Data) > 50 {
  149. return "base64:" + f.Base64Data[:50] + "..."
  150. }
  151. return "base64:" + f.Base64Data
  152. }
  153. // GetRawData 获取原始数据(URL 或完整的 base64 字符串)
  154. func (f *FileSource) GetRawData() string {
  155. if f.IsURL() {
  156. return f.URL
  157. }
  158. return f.Base64Data
  159. }
  160. // SetCache 设置缓存数据
  161. func (f *FileSource) SetCache(data *CachedFileData) {
  162. f.cachedData = data
  163. f.cacheLoaded = true
  164. }
  165. // IsRegistered 是否已注册到清理列表
  166. func (f *FileSource) IsRegistered() bool {
  167. return f.registered
  168. }
  169. // SetRegistered 设置注册状态
  170. func (f *FileSource) SetRegistered(registered bool) {
  171. f.registered = registered
  172. }
  173. // GetCache 获取缓存数据
  174. func (f *FileSource) GetCache() *CachedFileData {
  175. return f.cachedData
  176. }
  177. // HasCache 是否有缓存
  178. func (f *FileSource) HasCache() bool {
  179. return f.cacheLoaded && f.cachedData != nil
  180. }
  181. // ClearCache 清除缓存,释放内存和磁盘文件
  182. func (f *FileSource) ClearCache() {
  183. // 如果有缓存数据,先关闭它(会清理磁盘文件)
  184. if f.cachedData != nil {
  185. f.cachedData.Close()
  186. }
  187. f.cachedData = nil
  188. f.cacheLoaded = false
  189. }
  190. // ClearRawData 清除原始数据,只保留必要的元信息
  191. // 用于在处理完成后释放大文件的内存
  192. func (f *FileSource) ClearRawData() {
  193. // 保留 URL(通常很短),只清除大的 base64 数据
  194. if f.IsBase64() && len(f.Base64Data) > 1024 {
  195. f.Base64Data = ""
  196. }
  197. }