6.0 KiB
6.0 KiB
截图存储 Bug 修复说明
问题描述
在实施 Base64 截图存储方案后,发现了一个严重的 Bug:
错误信息: GET data:image/jpeg;base64:1 net::ERR_INVALID_URL
根本原因
问题分析
-
数据存储方式:使用逗号分隔的字符串存储多个截图
screenshots := strings.Join(customer.Screenshots, ",") -
数据读取方式:使用逗号分割字符串
c.Screenshots = strings.Split(screenshots.String, ",") -
Base64 数据特点:Base64 编码的字符串本身不包含逗号,但是...
-
Data URL 格式:
data:image/jpeg;base64,{base64_string}- 注意:逗号是 Data URL 格式的一部分!
- 格式:
data:{MIME类型};base64,{Base64数据}
问题示例
假设有两张截图:
原始数据:
[
"data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"data:image/png;base64,iVBORw0KGgoAAAA..."
]
存储到数据库(使用逗号连接):
data:image/jpeg;base64,/9j/4AAQSkZJRg...,data:image/png;base64,iVBORw0KGgoAAAA...
从数据库读取(使用逗号分割):
[
"data:image/jpeg;base64", // ❌ 被截断!
"/9j/4AAQSkZJRg...", // ❌ 不是有效的 Data URL
"data:image/png;base64", // ❌ 被截断!
"iVBORw0KGgoAAAA..." // ❌ 不是有效的 Data URL
]
结果:前端尝试加载 data:image/jpeg;base64 导致 ERR_INVALID_URL 错误!
解决方案
修改存储格式
从逗号分隔字符串改为 JSON 数组:
修改前(错误)
// 存储
screenshots := strings.Join(customer.Screenshots, ",")
// 读取
c.Screenshots = strings.Split(screenshots.String, ",")
修改后(正确)
// 存储
screenshotsJSON, err := json.Marshal(customer.Screenshots)
// 结果: ["data:image/jpeg;base64,...","data:image/png;base64,..."]
// 读取
var screenshotArray []string
json.Unmarshal([]byte(screenshots.String), &screenshotArray)
向后兼容
为了兼容旧数据(文件路径格式),添加了降级处理:
if screenshots.Valid && screenshots.String != "" {
// 尝试解析为 JSON 数组
var screenshotArray []string
if err := json.Unmarshal([]byte(screenshots.String), &screenshotArray); err == nil {
c.Screenshots = screenshotArray
} else {
// 向后兼容:如果不是 JSON,尝试逗号分隔(旧格式)
c.Screenshots = strings.Split(screenshots.String, ",")
}
} else {
c.Screenshots = []string{}
}
修改文件清单
/internal/storage/mysql_customer_storage.go
-
添加导入
import "encoding/json" -
修改
GetAllCustomers方法- 使用
json.Unmarshal解析 screenshots 字段 - 添加向后兼容逻辑
- 使用
-
修改
GetCustomerByID方法- 使用
json.Unmarshal解析 screenshots 字段 - 添加向后兼容逻辑
- 使用
-
修改
CreateCustomer方法- 使用
json.Marshal序列化 screenshots 数组
- 使用
-
修改
UpdateCustomer方法- 使用
json.Marshal序列化 screenshots 数组
- 使用
数据格式对比
旧格式(文件路径 - 使用逗号分隔)
数据库存储: /static/uploads/1.jpg,/static/uploads/2.png
解析结果: ["/static/uploads/1.jpg", "/static/uploads/2.png"]
状态: ✅ 正常(路径中没有逗号)
错误格式(Base64 - 使用逗号分隔)
数据库存储: data:image/jpeg;base64,abc...,data:image/png;base64,xyz...
解析结果: ["data:image/jpeg;base64", "abc...", "data:image/png;base64", "xyz..."]
状态: ❌ 错误(Data URL 被逗号截断)
新格式(JSON 数组)
数据库存储: ["data:image/jpeg;base64,abc...","data:image/png;base64,xyz..."]
解析结果: ["data:image/jpeg;base64,abc...", "data:image/png;base64,xyz..."]
状态: ✅ 正常(完整的 Data URL)
测试验证
1. 单元测试数据
// 测试 JSON 序列化
screenshots := []string{
"data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"data:image/png;base64,iVBORw0KGgoAAAA...",
}
json, _ := json.Marshal(screenshots)
fmt.Println(string(json))
// 输出: ["data:image/jpeg;base64,/9j/4AAQSkZJRg...","data:image/png;base64,iVBORw0KGgoAAAA..."]
// 测试 JSON 反序列化
var result []string
json.Unmarshal(json, &result)
fmt.Println(result)
// 输出: [data:image/jpeg;base64,/9j/4AAQSkZJRg... data:image/png;base64,iVBORw0KGgoAAAA...]
2. 集成测试
# 1. 上传截图
curl -X POST http://localhost:8081/api/upload \
-F "screenshots=@test.jpg"
# 2. 创建客户
curl -X POST http://localhost:8081/api/customers \
-H "Content-Type: application/json" \
-d '{
"customerName": "测试",
"screenshots": ["data:image/jpeg;base64,/9j/4AAQ..."]
}'
# 3. 查询客户
curl http://localhost:8081/api/customers/{id}
# 4. 验证数据库
mysql> SELECT screenshots FROM customers WHERE id = '{id}';
# 应该看到: ["data:image/jpeg;base64,/9j/4AAQ..."]
经验教训
1. 分隔符选择的重要性
当使用分隔符连接字符串时,必须确保:
- ✅ 分隔符不会出现在数据中
- ✅ 或者使用转义机制
- ✅ 或者使用结构化格式(如 JSON)
2. Data URL 格式
data:[<mediatype>][;base64],<data>
^ ^ ^
| | |
MIME类型 编码 逗号分隔符(重要!)
逗号是 Data URL 规范的一部分,不能用作数组分隔符!
3. 最佳实践
对于复杂数据结构,优先使用:
- JSON - 结构化、标准、易于解析
- Protocol Buffers - 高性能、类型安全
- 避免自定义分隔符 - 容易出错
4. 向后兼容
在修改数据格式时,务必考虑:
- 现有数据的迁移
- 降级处理逻辑
- 渐进式升级策略
总结
这个 Bug 的根本原因是:使用逗号分隔包含逗号的数据。
修复方案:使用 JSON 数组格式存储和解析数据。
这是一个典型的数据格式设计问题,提醒我们在设计数据存储格式时,必须充分考虑数据的特性和边界情况。