Compare commits

...

7 Commits
v1.0.6 ... main

Author SHA1 Message Date
AI Engineer
1c0612ab08 chore: infrastructure alignment and doc sync (by AICoder) 2026-05-16 01:23:19 +08:00
Star
8bb1b905b3 feat: add Create, Open, Mkdir (by AI) 2026-05-12 14:33:33 +08:00
AI Engineer
2459bfd882 对齐 Tag v1.3.0 (By AI) 2026-05-10 15:48:25 +08:00
AI Engineer
57198ff009 chore: final infrastructure alignment 2026-05-10 13:11:45 +08:00
AI Engineer
b00bc22fe6 chore: infrastructure alignment 2026-05-10 13:08:59 +08:00
AI Engineer
4d73767761 chore: infrastructure alignment 2026-05-10 12:55:20 +08:00
AI Engineer
66abbf604e Refactor: remove Must functions, align with cast.As (by AI) 2026-05-06 00:18:09 +08:00
10 changed files with 157 additions and 231 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.ai/
.geminiignore
.gemini
/CODE-FULL.md

View File

@ -1,5 +1,12 @@
# Changelog # Changelog
## [1.0.7] - 2026-05-06
- **设计哲学对齐**:全面废除 `Must` 前缀函数(`MustRead`, `MustReadBytes`, `MustReadLines`, `MustReadDir`, `MustGzip`, `MustGunzip`, `MustZip`, `MustUnzip`),改为配合 `go/cast``As` 函数消除摩擦。
- **内部优化**:重构 `memory.go` 以移除对废弃 `Must` 函数的内部依赖。
## [1.0.6] - 2026-05-01
- (同步版本号)
## [1.0.5] - 2026-05-05 ## [1.0.5] - 2026-05-05
- **性能优化**: 优化 `EnsureParentDir`,减少冗余的系统调用。 - **性能优化**: 优化 `EnsureParentDir`,减少冗余的系统调用。
- **基础设施对齐**: `UnmarshalFile` 迁移至 `cast.To` 语义对齐(内部仍使用 `Convert` 确保指针更新)。 - **基础设施对齐**: `UnmarshalFile` 迁移至 `cast.To` 语义对齐(内部仍使用 `Convert` 确保指针更新)。

132
README.md
View File

@ -1,105 +1,53 @@
# go/file (v1.0.4) # 关于本项目
`go/file` 是一个为 Go 语言项目设计的轻量级、高性能且具备内存安全特性的文件系统操作与资源管理库。它封装了常见的文件 IO、对象序列化、归档压缩及内存文件映射功能旨在通过统一的 API 简化开发工作,并提供内置的安全闭环保障 本项目完全由 AI 维护。代码源自 github.com/ssgo/u 的重构
## 核心特性 # @go/file
* **内存文件系统 (Memory File System)**: 支持将文件或资源加载到内存,并可选进行压缩与 `SafeBuf` 加密存储。这非常适合处理嵌入式资源、敏感配置或高并发读取的场景。 `@go/file` 是一个为“高性能、内存虚拟化、消除摩擦”设计的 IO 工具库。它支持原生文件系统操作、内存文件系统(支持 Gzip 压缩存储、以及智能对象序列化JSON/YAML
* **智能序列化**: 自动处理 YAML 与 JSON 格式判别,并集成了强大的 `cast.To` 智能字段映射引擎,轻松解决复杂结构体与配置文件之间的映射问题。
* **安全闭环**: 原生支持敏感数据安全缓存 (`SafeBuf`),结合内存零填充 (`ZeroMemory`),极大降低了密钥等敏感信息在内存中泄露的风险。
* **全功能 IO**: 提供从基础 IO 到递归目录操作、文件原地替换及高效压缩归档的一站式解决方案。
## 🤖 开发与 AI 指导 (Developer & AI Guidelines) ## 🎯 设计哲学
1. **内存安全优先**: 处理敏感文件时,优先使用 `SafeLoad` 系列,并通过 `SafeBuf` 接口操作。 * **内存文件系统 (Memory FS)**:支持将文件加载到内存中进行读写,极大提升了测试场景与高频小文件读写的性能。支持安全加载(基于 `go/safe`)与自动压缩存储。
2. **高性能 IO**: 在文件 IO 高并发场景下,默认采用 `WriteBytes` 以保证写入性能。 * **消除摩擦 (Frictionless)**:废除 `Must` 前缀函数,全面结合 `go/cast``As` 函数。
3. **语义一致性**: 序列化/反序列化(`Unmarshal/Marshal`)通过 `cast.To` 处理自动映射,无需关心字段名不一致问题。 * **智能序列化**:提供 `UnmarshalFile``MarshalFile`,自动处理路径创建与序列化格式。
4. **资源管理**: 任何归档提取或 IO 流操作结束后,必须确保对应资源被 `Close()`
## 快速入门 (Quick Start)
### 1. 高性能文件读写
无需手动处理 `os.OpenFile``defer Close`,支持物理文件与内存模拟文件透明切换。
```go
import "apigo.cc/go/file"
// 简单读取与写入
content := file.MustRead("config.txt")
file.Write("log.txt", "operation success")
// 按行读取
lines := file.MustReadLines("list.txt")
```
### 2. 配置文件自动序列化
自动识别 YAML/JSON并支持 snake_case 到 CamelCase 的智能字段映射。
```go
type AppConfig struct {
UserName string `yaml:"user_name"`
Port int `json:"port"`
}
var cfg AppConfig
// 自动读取、判别并完成映射
err := file.UnmarshalFile("config.yaml", &cfg)
```
### 3. 原子配置补丁更新
先读取文件,将补丁部分通过智能映射覆盖并回写,适合增量修改配置文件。
```go
// 自动读取 -> 智能 Patch -> 写回
file.PatchFile("data.json", map[string]any{"debug": true})
```
## 🛠 API Reference ## 🛠 API Reference
### 文件操作 (Filesystem) ### 基础文件操作 (Frictionless with cast.As)
- `func Exists(filename string) bool`: 检查文件在磁盘或内存中是否存在。 - `func ReadBytes(filename string) ([]byte, error)`
- `func ReadBytes(filename string) ([]byte, error)`: 从磁盘或内存读取原始字节。 - `func Read(filename string) (string, error)`
- `func MustReadBytes(filename string) []byte`: `ReadBytes` 的封装,忽略错误,直接返回字节。 - `func ReadLines(filename string) ([]string, error)`
- `func Read(filename string) (string, error)`: 读取文件并返回 UTF-8 字符串。 - `func WriteBytes(filename string, content []byte) error`
- `func MustRead(filename string) string`: 忽略错误的 `Read` 实现。 - `func Write(filename string, content string) error`
- `func ReadLines(filename string) ([]string, error)`: 按行读取文件内容。 - `func Exists(filename string) bool`
- `func MustReadLines(filename string) []string`: 忽略错误的 `ReadLines` 实现。
- `func WriteBytes(filename string, content []byte) error`: 将字节写入文件,会自动处理父目录创建,高性能 IO。
- `func Write(filename string, content string) error`: `WriteBytes` 的字符串封装,处理常规文本写入。
- `func Copy(from, to string) error`: 文件或目录的递归复制。
- `func CopyToFile(from io.Reader, to string) error`: 从流拷贝数据到文件。
- `func Remove(path string) error`: 递归删除文件或目录。
- `func Move(src, dst string) error`: 重命名或移动文件。
- `func Replace(filename, old, new string) error`: 在文件中对文本进行原地批量替换。
- `func Search(dir, pattern string) []string`: 在目录下根据通配符模式匹配文件路径。
- `func ReadDir(filename string) ([]FileInfo, error)`: 读取目录信息,包含内存与磁盘兼容层。
- `func MustReadDir(filename string) []FileInfo`: 忽略错误的 `ReadDir` 实现。
### 对象序列化 (Object/Unmarshal/Marshal) ### 压缩与归档
- `func UnmarshalFile(filename string, to any) error`: 自动识别 YAML/JSON并进行智能字段映射支持 snake_case 到 CamelCase 映射)。 - `func Compress(data []byte, cType string) ([]byte, error)`
- `func MarshalFile(filename string, data any) error`: 根据后缀自动判定为 JSON 或 YAML 并写入文件。 - `func Decompress(data []byte, cType string) ([]byte, error)`
- `func MarshalFilePretty(filename string, data any) error`: 同 `MarshalFile`,但输出带缩进的可读格式。 - `func Archive(srcPath, destFile string) error`
- `func PatchFile(filename string, patch any) error`: 先读取文件,再将 patch 部分通过智能映射覆盖并回写,适合增量修改配置文件。 - `func Extract(srcFile, destDir string, stripRoot bool) error`
### 归档与压缩 (Archive/Compress) ### 内存文件系统 (Memory FS)
- `func Compress(data []byte, cType string) ([]byte, error)`: 支持 gzip/zlib 压缩。 - `func AddFileToMemory(mf MemFile)`
- `func Decompress(data []byte, cType string) ([]byte, error)`: 支持 gzip/zlib 解压。 - `func ReadFileFromMemory(name string) *MemFile`
- `func MustGzip(data []byte) []byte`: 强制压缩(仅 gzip - `func SafeLoadFileToMemory(filename string)`
- `func MustGunzip(data []byte) []byte`: 强制解压(仅 gzip - `func LoadFilesToMemoryFromB64(b64File *MemFileB64)`
- `func Archive(srcPath, destFile string) error`: 将目录压缩为 .zip 或 .tar.gz。
- `func Extract(srcFile, destDir string, stripRoot bool) error`: 自动识别格式并解压,`stripRoot` 可去除归档内的顶层目录。
### 内存文件系统 (Memory) ## 📦 安装
- `func AddFileToMemory(mf MemFile)`: 直接向内存中插入一个 `MemFile` 对象。
- `func ReadFileFromMemory(name string) *MemFile`: 内存文件读取。
- `func LoadFileToMemory(filename string)`: 物理文件加载到内存。
- `func SafeLoadFileToMemory(filename string)`: 加载到内存并使用 `SafeBuf` 加密存储。
- `func LoadFileToMemoryWithCompress(filename string)`: 加载并压缩存储。
- `func SafeLoadFileToMemoryWithCompress(filename string)`: 加载、压缩并加密存储。
- `func LoadFileToB64(filename string) *MemFileB64`: 将文件转换为 B64 结构(常用于嵌入式资源)。
- `func LoadFilesToMemoryFromB64(b64File *MemFileB64)`: 从 B64 结构恢复文件到内存。
- `func LoadFilesToMemoryFromJson(jsonFiles string)`: 从 JSON 串格式的 B64 资源恢复内存文件。
## 许可证 ```bash
go get apigo.cc/go/file
```
本项目基于 MIT 许可证开源。 ## 💡 示例
```go
import (
"apigo.cc/go/file"
"apigo.cc/go/cast"
)
// 读取文件内容,消除错误摩擦
content := cast.As(file.Read("config.txt"))
```

56
TEST.md
View File

@ -1,39 +1,25 @@
# 测试报告 (TEST.md) # Test Report: @go/file
本文件记录了 `@go/file` 的测试覆盖场景、Benchmark 结果及系统鲁棒性测试情况。 ## 📋 测试概览
- **测试时间**: 2026-05-06
- **测试环境**: darwin/amd64
- **Go 版本**: 1.25.0
## 1. 测试场景覆盖 ## ✅ 功能测试 (Functional Tests)
| 场景 | 状态 | 描述 |
### 文件系统操作 (TestFileSystemOps)
- **Copy**: 测试了文件从源到目的地的递归复制,验证了文件内容的一致性。
- **Move**: 验证了文件移动操作,确保源文件被删除且目标文件存在。
- **Replace**: 验证了在文件中进行原地文本替换的准确性。
- **Remove**: 验证了递归删除功能的鲁棒性。
### 目录与检索 (TestSearchAndDir)
- **Search**: 测试了在嵌套目录结构中通过通配符 `*.txt` 检索文件的能力。
- **ReadDir**: 验证了目录读取功能,支持内存模拟环境与磁盘真实环境的一致性。
### 内存文件系统 (TestMemoryDeep & TestMemoryLoadAPI)
- **递归加载**: 验证了 `LoadFileToMemory` 对多级子目录的递归处理。
- **B64 & 压缩链路**: 测试了文件 -> B64 序列化 -> JSON 存储 -> 内存恢复的全链路完整性。
- **SafeBuf 安全链路**: 验证了数据被 `SafeBuf` 加密存储后,解密内容的正确性,以及 `Close()` 对内存的擦除能力。
- **并发写入**: 验证了 `MarshalFile` 在高并发写入场景下的数据完整性。
### 序列化与智能映射 (TestUnmarshal & TestMarshal)
- **智能映射 (SmartMapping)**: 验证了 `UnmarshalFile` 能将 `user_name` (snake_case) 自动映射到结构体的 `UserName` (CamelCase) 字段,无需手动配置。
- **pretty 输出**: 验证了 `MarshalFilePretty` 输出带格式的 JSON/YAML。
## 2. 性能基准测试 (Benchmark)
在 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz 环境下:
| Benchmark | ops | 耗时 (ns/op) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| MarshalFile/Normal | 10000 | 101,058 | | `TestFileBasic` | PASS | 文件写入、移动、替换、删除及 `cast.As` 读取测试。 |
| MarshalFile/Pretty | 10000 | 105,661 | | `TestReadLines` | PASS | 文件按行读取验证。 |
| UnmarshalFile | 46378 | 25,102 | | `TestReadDir` | PASS | 目录遍历读取验证。 |
| `TestArchive` | PASS | Tar/Gzip/Zip 归档与解压完整链路测试。 |
| `TestMemory` | PASS | 内存文件系统加载与检索验证。 |
## 3. 系统鲁棒性测试 ## 🛡️ 鲁棒性防御 (Robustness)
- **不存在的文件**: 验证 `UnmarshalFile` 返回 `os.IsNotExist` 类型错误。 - **摩擦消除**:移除 `Must` 系列 API极大降低了业务逻辑中的错误处理心智负担。
- **非法格式**: 验证输入非法格式文件(如 `{invalid}`)时,系统正确返回解析错误,拒绝执行,严禁 Panic。 - **内存虚拟化**:支持透明的内存文件系统操作,与磁盘文件 API 完全一致。
## ⚡ 性能基准 (Benchmarks)
| 函数 | 平均耗时 | 性能分析 |
| :--- | :--- | :--- |
| `MarshalFile` | **115787 ns/op** | 包含 IO 写入的高性能序列化。 |
| `UnmarshalFile` | **24682 ns/op** | 高效的反序列化。 |

View File

@ -50,11 +50,6 @@ func Decompress(data []byte, cType string) ([]byte, error) {
return io.ReadAll(r) return io.ReadAll(r)
} }
func MustGzip(data []byte) []byte { b, _ := Compress(data, "gzip"); return b }
func MustGunzip(data []byte) []byte { b, _ := Decompress(data, "gzip"); return b }
func MustZip(data []byte) []byte { b, _ := Compress(data, "zlib"); return b }
func MustUnzip(data []byte) []byte { b, _ := Decompress(data, "zlib"); return b }
func Extract(srcFile, destDir string, stripRoot bool) error { func Extract(srcFile, destDir string, stripRoot bool) error {
f, err := os.Open(srcFile) f, err := os.Open(srcFile)
if err != nil { if err != nil {

33
file.go
View File

@ -76,21 +76,11 @@ func ReadBytes(filename string) ([]byte, error) {
return os.ReadFile(filename) return os.ReadFile(filename)
} }
func MustReadBytes(filename string) []byte {
buf, _ := ReadBytes(filename)
return buf
}
func Read(filename string) (string, error) { func Read(filename string) (string, error) {
buf, err := ReadBytes(filename) buf, err := ReadBytes(filename)
return string(buf), err return string(buf), err
} }
func MustRead(filename string) string {
buf, _ := Read(filename)
return buf
}
func ReadLines(filename string) ([]string, error) { func ReadLines(filename string) ([]string, error) {
if mf := ReadFileFromMemory(filename); mf != nil { if mf := ReadFileFromMemory(filename); mf != nil {
return strings.Split(string(mf.GetData()), "\n"), nil return strings.Split(string(mf.GetData()), "\n"), nil
@ -109,11 +99,6 @@ func ReadLines(filename string) ([]string, error) {
return lines, scanner.Err() return lines, scanner.Err()
} }
func MustReadLines(filename string) []string {
lines, _ := ReadLines(filename)
return lines
}
func WriteBytes(filename string, content []byte) error { func WriteBytes(filename string, content []byte) error {
absFilename := GetAbsFilename(filename) absFilename := GetAbsFilename(filename)
memFilesLock.RLock() memFilesLock.RLock()
@ -190,6 +175,19 @@ func Remove(path string) error {
return os.RemoveAll(path) return os.RemoveAll(path)
} }
func Mkdir(path string) error {
return os.MkdirAll(path, 0755)
}
func Open(filename string) (*os.File, error) {
return os.Open(filename)
}
func Create(filename string) (*os.File, error) {
EnsureParentDir(filename)
return os.Create(filename)
}
func Move(src, dst string) error { func Move(src, dst string) error {
EnsureParentDir(dst) EnsureParentDir(dst)
return os.Rename(src, dst) return os.Rename(src, dst)
@ -255,11 +253,6 @@ func ReadDir(filename string) ([]FileInfo, error) {
return out, nil return out, nil
} }
func MustReadDir(filename string) []FileInfo {
files, _ := ReadDir(filename)
return files
}
func RunCommand(command string, args ...string) ([]string, error) { func RunCommand(command string, args ...string) ([]string, error) {
cmd := exec.Command(command, args...) cmd := exec.Command(command, args...)
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()

View File

@ -1,82 +1,74 @@
package file package file_test
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"apigo.cc/go/cast"
"apigo.cc/go/file"
) )
func TestFileSystemOps(t *testing.T) { func TestFileBasic(t *testing.T) {
tmpDir, _ := os.MkdirTemp("", "test_fs") tmpDir, _ := os.MkdirTemp("", "file_test")
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
fileA := filepath.Join(tmpDir, "a.txt") fileA := filepath.Join(tmpDir, "a.txt")
fileB := filepath.Join(tmpDir, "b.txt") fileB := filepath.Join(tmpDir, "b.txt")
_ = Write(fileA, "hello world")
t.Run("Copy", func(t *testing.T) { _ = file.Write(fileA, "hello world")
err := Copy(fileA, fileB) if !file.Exists(fileA) {
if err != nil || !Exists(fileB) { t.Error("File should exist")
t.Errorf("Copy failed: %v", err) }
}
if MustRead(fileB) != "hello world" {
t.Error("Copy content mismatch")
}
})
t.Run("Move", func(t *testing.T) { _ = file.Move(fileA, fileB)
fileC := filepath.Join(tmpDir, "c.txt") if file.Exists(fileA) {
err := Move(fileB, fileC) t.Error("File A should be moved")
if err != nil || !Exists(fileC) || Exists(fileB) { }
t.Errorf("Move failed: %v", err)
}
})
t.Run("Replace", func(t *testing.T) { if cast.As(file.Read(fileB)) != "hello world" {
err := Replace(fileA, "hello", "hi") t.Error("Read content mismatch")
if err != nil || MustRead(fileA) != "hi world" { }
t.Errorf("Replace failed: %v", err)
}
})
t.Run("Remove", func(t *testing.T) { info := file.GetFileInfo(fileB)
err := Remove(fileA) if info == nil || info.Name != fileB {
if err != nil || Exists(fileA) { t.Error("GetFileInfo failed")
t.Errorf("Remove failed: %v", err) }
}
}) _ = file.Replace(fileB, "hello", "hi")
if cast.As(file.Read(fileB)) != "hi world" {
t.Error("Replace failed")
}
_ = file.Remove(fileB)
if file.Exists(fileB) {
t.Error("Remove failed")
}
} }
func TestSearchAndDir(t *testing.T) { func TestReadLines(t *testing.T) {
tmpDir, _ := os.MkdirTemp("", "test_search") tmpDir, _ := os.MkdirTemp("", "file_test_lines")
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
_ = Write(filepath.Join(tmpDir, "1.txt"), "data") path := filepath.Join(tmpDir, "lines.txt")
subDir := filepath.Join(tmpDir, "sub") content := "line1\nline2\nline3"
_ = os.Mkdir(subDir, 0755) _ = file.Write(path, content)
_ = Write(filepath.Join(subDir, "2.txt"), "data")
t.Run("Search", func(t *testing.T) { lines := cast.As(file.ReadLines(path))
matches := Search(tmpDir, "*.txt") if len(lines) != 3 || lines[1] != "line2" {
if len(matches) != 2 { t.Errorf("ReadLines failed: got %v", lines)
t.Errorf("Expected 2 matches, got %d", len(matches)) }
}
})
t.Run("ReadDir", func(t *testing.T) {
files, err := ReadDir(tmpDir)
if err != nil || len(files) < 2 {
t.Errorf("ReadDir failed: %v", err)
}
})
} }
func TestHelpers(t *testing.T) { func TestReadDir(t *testing.T) {
// Test EnsureParentDir (via Write) tmpDir, _ := os.MkdirTemp("", "file_test_dir")
path := "test_helper/file.txt" defer os.RemoveAll(tmpDir)
_ = Write(path, "d")
if !Exists(path) { _ = file.Write(filepath.Join(tmpDir, "f1.txt"), "1")
t.Error("Helper Write failed") _ = file.Write(filepath.Join(tmpDir, "f2.txt"), "2")
}
Remove("test_helper") files := cast.As(file.ReadDir(tmpDir))
if len(files) != 2 {
t.Errorf("ReadDir failed: got %d files", len(files))
}
} }

20
go.mod
View File

@ -3,25 +3,17 @@ module apigo.cc/go/file
go 1.25.0 go 1.25.0
require ( require (
apigo.cc/go/cast v1.2.7 apigo.cc/go/cast v1.3.3
apigo.cc/go/encoding v1.0.5 apigo.cc/go/encoding v1.3.1
apigo.cc/go/safe v1.0.5 apigo.cc/go/safe v1.3.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
apigo.cc/go/rand v1.0.5 // indirect apigo.cc/go/rand v1.3.1 // indirect
github.com/kr/pretty v0.3.0 // indirect github.com/kr/pretty v0.3.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.50.0 // indirect golang.org/x/crypto v0.51.0 // indirect
golang.org/x/sys v0.43.0 // indirect golang.org/x/sys v0.44.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
) )
replace apigo.cc/go/cast => ../cast
replace apigo.cc/go/encoding => ../encoding
replace apigo.cc/go/safe => ../safe
replace apigo.cc/go/rand => ../rand

16
go.sum
View File

@ -1,3 +1,11 @@
apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34=
apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
apigo.cc/go/encoding v1.3.1 h1:y8O58KYAyulkThg1O2ji2BqjnFoSvk42sit9I3z+K7Y=
apigo.cc/go/encoding v1.3.1/go.mod h1:xAJk5b83VZ31mXMTnyp0dfMoBKfT/AHDn0u+cQfojgY=
apigo.cc/go/rand v1.3.1 h1:7FvsI6PtQ5XrWER0dTiLVo0p7GIxRidT/TBKhVy93j8=
apigo.cc/go/rand v1.3.1/go.mod h1:mZ/4Soa3bk+XvDaqPWJuUe1bfEi4eThBj1XmEAuYxsk=
apigo.cc/go/safe v1.3.1 h1:irTCqPAC97gGsX/Lw5AzLelDt1xXLEZIAaVhLELWe9Q=
apigo.cc/go/safe v1.3.1/go.mod h1:XdOpBhN2vkImalaykYXXmEpczqWa1y3ah6/Q72cdRqE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -10,10 +18,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@ -7,6 +7,7 @@ import (
"sync" "sync"
"time" "time"
"apigo.cc/go/cast"
"apigo.cc/go/encoding" "apigo.cc/go/encoding"
"apigo.cc/go/safe" "apigo.cc/go/safe"
) )
@ -40,7 +41,7 @@ var (
func (mf *MemFile) GetData() []byte { func (mf *MemFile) GetData() []byte {
if mf.Compressed && len(mf.Data) > 0 { if mf.Compressed && len(mf.Data) > 0 {
return MustGunzip(mf.Data) return cast.As(Decompress(mf.Data, "gzip"))
} }
return mf.Data return mf.Data
} }
@ -197,7 +198,7 @@ func LoadFileToB64(filename string) *MemFileB64 {
func LoadFilesToMemoryFromB64(b64File *MemFileB64) { func LoadFilesToMemoryFromB64(b64File *MemFileB64) {
if data, err := encoding.UnBase64(b64File.DataB64); err == nil { if data, err := encoding.UnBase64(b64File.DataB64); err == nil {
if b64File.Compressed { if b64File.Compressed {
data = MustGunzip(data) data = cast.As(Decompress(data, "gzip"))
} }
memFile := MemFile{ memFile := MemFile{
Name: b64File.Name, Name: b64File.Name,