File size: 6,017 Bytes
daa8246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package types

import (
	"fmt"
	"image"
	"os"
	"sync"
)

// FileSourceType 文件来源类型
type FileSourceType string

const (
	FileSourceTypeURL    FileSourceType = "url"    // URL 来源
	FileSourceTypeBase64 FileSourceType = "base64" // Base64 内联数据
)

// FileSource 统一的文件来源抽象
// 支持 URL 和 base64 两种来源,提供懒加载和缓存机制
type FileSource struct {
	Type       FileSourceType `json:"type"`                  // 来源类型
	URL        string         `json:"url,omitempty"`         // URL(当 Type 为 url 时)
	Base64Data string         `json:"base64_data,omitempty"` // Base64 数据(当 Type 为 base64 时)
	MimeType   string         `json:"mime_type,omitempty"`   // MIME 类型(可选,会自动检测)

	// 内部缓存(不导出,不序列化)
	cachedData  *CachedFileData
	cacheLoaded bool
	registered  bool       // 是否已注册到清理列表
	mu          sync.Mutex // 保护加载过程
}

// Mu 获取内部锁
func (f *FileSource) Mu() *sync.Mutex {
	return &f.mu
}

// CachedFileData 缓存的文件数据
// 支持内存缓存和磁盘缓存两种模式
type CachedFileData struct {
	base64Data  string        // 内存中的 base64 数据(小文件)
	MimeType    string        // MIME 类型
	Size        int64         // 文件大小(字节)
	DiskSize    int64         // 磁盘缓存实际占用大小(字节,通常是 base64 长度)
	ImageConfig *image.Config // 图片配置(如果是图片)
	ImageFormat string        // 图片格式(如果是图片)

	// 磁盘缓存相关
	diskPath        string     // 磁盘缓存文件路径(大文件)
	isDisk          bool       // 是否使用磁盘缓存
	diskMu          sync.Mutex // 磁盘操作锁(保护磁盘文件的读取和删除)
	diskClosed      bool       // 是否已关闭/清理
	statDecremented bool       // 是否已扣减统计

	// 统计回调,避免循环依赖
	OnClose func(size int64)
}

// NewMemoryCachedData 创建内存缓存的数据
func NewMemoryCachedData(base64Data string, mimeType string, size int64) *CachedFileData {
	return &CachedFileData{
		base64Data: base64Data,
		MimeType:   mimeType,
		Size:       size,
		isDisk:     false,
	}
}

// NewDiskCachedData 创建磁盘缓存的数据
func NewDiskCachedData(diskPath string, mimeType string, size int64) *CachedFileData {
	return &CachedFileData{
		diskPath: diskPath,
		MimeType: mimeType,
		Size:     size,
		isDisk:   true,
	}
}

// GetBase64Data 获取 base64 数据(自动处理内存/磁盘)
func (c *CachedFileData) GetBase64Data() (string, error) {
	if !c.isDisk {
		return c.base64Data, nil
	}

	c.diskMu.Lock()
	defer c.diskMu.Unlock()

	if c.diskClosed {
		return "", fmt.Errorf("disk cache already closed")
	}

	// 从磁盘读取
	data, err := os.ReadFile(c.diskPath)
	if err != nil {
		return "", fmt.Errorf("failed to read from disk cache: %w", err)
	}
	return string(data), nil
}

// SetBase64Data 设置 base64 数据(仅用于内存模式)
func (c *CachedFileData) SetBase64Data(data string) {
	if !c.isDisk {
		c.base64Data = data
	}
}

// IsDisk 是否使用磁盘缓存
func (c *CachedFileData) IsDisk() bool {
	return c.isDisk
}

// Close 关闭并清理资源
func (c *CachedFileData) Close() error {
	if !c.isDisk {
		c.base64Data = "" // 释放内存
		return nil
	}

	c.diskMu.Lock()
	defer c.diskMu.Unlock()

	if c.diskClosed {
		return nil
	}

	c.diskClosed = true
	if c.diskPath != "" {
		err := os.Remove(c.diskPath)
		// 只有在删除成功且未扣减过统计时,才执行回调
		if err == nil && !c.statDecremented && c.OnClose != nil {
			c.OnClose(c.DiskSize)
			c.statDecremented = true
		}
		return err
	}
	return nil
}

// NewURLFileSource 创建 URL 来源的 FileSource
func NewURLFileSource(url string) *FileSource {
	return &FileSource{
		Type: FileSourceTypeURL,
		URL:  url,
	}
}

// NewBase64FileSource 创建 base64 来源的 FileSource
func NewBase64FileSource(base64Data string, mimeType string) *FileSource {
	return &FileSource{
		Type:       FileSourceTypeBase64,
		Base64Data: base64Data,
		MimeType:   mimeType,
	}
}

// IsURL 判断是否是 URL 来源
func (f *FileSource) IsURL() bool {
	return f.Type == FileSourceTypeURL
}

// IsBase64 判断是否是 base64 来源
func (f *FileSource) IsBase64() bool {
	return f.Type == FileSourceTypeBase64
}

// GetIdentifier 获取文件标识符(用于日志和错误追踪)
func (f *FileSource) GetIdentifier() string {
	if f.IsURL() {
		if len(f.URL) > 100 {
			return f.URL[:100] + "..."
		}
		return f.URL
	}
	if len(f.Base64Data) > 50 {
		return "base64:" + f.Base64Data[:50] + "..."
	}
	return "base64:" + f.Base64Data
}

// GetRawData 获取原始数据(URL 或完整的 base64 字符串)
func (f *FileSource) GetRawData() string {
	if f.IsURL() {
		return f.URL
	}
	return f.Base64Data
}

// SetCache 设置缓存数据
func (f *FileSource) SetCache(data *CachedFileData) {
	f.cachedData = data
	f.cacheLoaded = true
}

// IsRegistered 是否已注册到清理列表
func (f *FileSource) IsRegistered() bool {
	return f.registered
}

// SetRegistered 设置注册状态
func (f *FileSource) SetRegistered(registered bool) {
	f.registered = registered
}

// GetCache 获取缓存数据
func (f *FileSource) GetCache() *CachedFileData {
	return f.cachedData
}

// HasCache 是否有缓存
func (f *FileSource) HasCache() bool {
	return f.cacheLoaded && f.cachedData != nil
}

// ClearCache 清除缓存,释放内存和磁盘文件
func (f *FileSource) ClearCache() {
	// 如果有缓存数据,先关闭它(会清理磁盘文件)
	if f.cachedData != nil {
		f.cachedData.Close()
	}
	f.cachedData = nil
	f.cacheLoaded = false
}

// ClearRawData 清除原始数据,只保留必要的元信息
// 用于在处理完成后释放大文件的内存
func (f *FileSource) ClearRawData() {
	// 保留 URL(通常很短),只清除大的 base64 数据
	if f.IsBase64() && len(f.Base64Data) > 1024 {
		f.Base64Data = ""
	}
}