Skip to content

Commit d728803

Browse files
committed
v2,v3增加缓存功能, 大幅提升xlsx格式导出速度
1 parent f40af96 commit d728803

17 files changed

+318
-31
lines changed

doc/Manual_V2.md

+11
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,17 @@
351351

352352
* 范例: 语言名支持en_us(默认), zh_cn(简体中文)
353353

354+
## 并发导出
355+
356+
* 格式: --para=true
357+
358+
* 功能: 使用多线程技术并发导出, 提升导出速度
359+
360+
## 使用缓冲
361+
362+
* 格式: --usecache=true
363+
364+
* 功能: 导出时, 使用缓冲, 大幅提升复杂xlsx格式导出速度, 导出目录使用--cachedir设置, 默认目录为/.tabtoycache
354365

355366
## 多文件合并
356367

entry_v2.go

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func V2Entry() {
3333
}
3434

3535
g.ParaMode = *paramPara
36+
g.CacheDir = *paramCacheDir
37+
g.UseCache = *paramUseCache
3638
g.CombineStructName = *paramCombineStructName
3739
g.ProtoVersion = *paramProtoVersion
3840
g.LuaEnumIntValue = *paramLuaEnumIntValue

entry_v2tov3.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func V2ToV3Entry() {
1616

1717
globals := model.NewGlobals()
1818

19-
globals.TableGetter = helper.NewFileLoader(true)
19+
globals.TableGetter = helper.NewFileLoader(true, "")
2020

2121
globals.SourceFileList = flag.Args()
2222
globals.OutputDir = *paramUpgradeOut

entry_v3.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ func V3Entry() {
8686
globals := model.NewGlobals()
8787
globals.Version = Version
8888
globals.ParaLoading = *paramPara
89+
if *paramUseCache {
90+
globals.CacheDir = *paramCacheDir
91+
os.Mkdir(globals.CacheDir, 0666)
92+
}
8993
globals.IndexFile = *paramIndexFile
9094
globals.PackageName = *paramPackageName
9195
globals.CombineStructName = *paramCombineStructName
@@ -96,7 +100,7 @@ func V3Entry() {
96100
report.Log.Infof("MatchTag: %s", globals.MatchTag)
97101
}
98102

99-
idxloader := helper.NewFileLoader(true)
103+
idxloader := helper.NewFileLoader(true, globals.CacheDir)
100104
idxloader.UseGBKCSV = *paramUseGBKCSV
101105
globals.IndexGetter = idxloader
102106
globals.UseGBKCSV = *paramUseGBKCSV

flag.go

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ var (
1313
// 并发导出,提高导出速度, 输出日志会混乱
1414
paramPara = flag.Bool("para", false, "parallel export by your cpu count")
1515

16+
// 并发导出,提高导出速度, 输出日志会混乱
17+
paramCacheDir = flag.String("cachedir", "./.tabtoycache", "cache file output dir")
18+
paramUseCache = flag.Bool("usecache", false, "use cache file enhanced exporting speed")
19+
1620
paramLanguage = flag.String("lan", "en_us", "set output language")
1721
)
1822

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
var log = golog.New("main")
1212

1313
const (
14-
Version = "2.9.3"
14+
Version = "2.9.4"
1515
)
1616

1717
var enableProfile bool = false

util/cache.go

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package util
2+
3+
import (
4+
"archive/zip"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/tealeg/xlsx"
8+
"os"
9+
)
10+
11+
type XlsxHashFile struct {
12+
CRC32Map map[string]uint32
13+
}
14+
15+
type XlsxFileCache struct {
16+
Name string
17+
Sheets []*XlsxSheet
18+
}
19+
20+
type XlsxSheet struct {
21+
Name string
22+
Cells [][]string
23+
}
24+
25+
type TableCache struct {
26+
z *zip.ReadCloser
27+
name string
28+
cacheDir string
29+
30+
originFile *xlsx.File
31+
}
32+
33+
func NewTableCache(name, cachedir string) *TableCache {
34+
return &TableCache{
35+
name: name,
36+
cacheDir: cachedir,
37+
}
38+
}
39+
40+
func (self *TableCache) UseCache() bool {
41+
return self.originFile == nil
42+
}
43+
44+
func (self *TableCache) cacheFileName() string {
45+
return fmt.Sprintf("%s/%s.cache", self.cacheDir, self.name)
46+
}
47+
48+
func (self *TableCache) hashFileName() string {
49+
return fmt.Sprintf("%s/%s.hash", self.cacheDir, self.name)
50+
}
51+
52+
func (self *TableCache) Open() error {
53+
54+
z, err := zip.OpenReader(self.name)
55+
if err != nil {
56+
return err
57+
}
58+
59+
self.z = z
60+
61+
return nil
62+
}
63+
64+
func readJsonFile(filename string, m interface{}) error {
65+
f, err := os.Open(filename)
66+
if err != nil {
67+
return nil
68+
}
69+
70+
return json.NewDecoder(f).Decode(m)
71+
}
72+
73+
func writeJsonFile(filename string, m interface{}) error {
74+
f, err := os.Create(filename)
75+
if err != nil {
76+
return err
77+
}
78+
79+
return json.NewEncoder(f).Encode(m)
80+
}
81+
82+
func (self *TableCache) readCache() (xf *xlsx.File, err error) {
83+
84+
var hashFile XlsxHashFile
85+
err = readJsonFile(self.hashFileName(), &hashFile)
86+
if err != nil {
87+
return nil, nil
88+
}
89+
90+
if len(self.z.File) != len(hashFile.CRC32Map) {
91+
return nil, nil
92+
}
93+
94+
for _, f := range self.z.File {
95+
if hashFile.CRC32Map[f.Name] != f.CRC32 {
96+
return nil, nil
97+
}
98+
}
99+
100+
var cacheFile XlsxFileCache
101+
err = readJsonFile(self.cacheFileName(), &cacheFile)
102+
103+
if err != nil {
104+
return nil, nil
105+
}
106+
107+
xf = xlsx.NewFile()
108+
109+
for _, s := range cacheFile.Sheets {
110+
sheet, err := xf.AddSheet(s.Name)
111+
if err != nil {
112+
return nil, err
113+
}
114+
for _, srcRow := range s.Cells {
115+
tgtRow := sheet.AddRow()
116+
117+
for _, c := range srcRow {
118+
cell := tgtRow.AddCell()
119+
cell.Value = c
120+
}
121+
122+
}
123+
}
124+
125+
return
126+
}
127+
128+
func (self *TableCache) Load() (xf *xlsx.File, err error) {
129+
130+
cfile, err := self.readCache()
131+
132+
// cache未击中, 从原文件读取
133+
if err == nil && cfile == nil {
134+
xf, err = xlsx.ReadZipWithRowLimit(self.z, xlsx.NoRowLimit)
135+
self.originFile = xf
136+
return
137+
}
138+
139+
return cfile, err
140+
}
141+
142+
func (self *TableCache) Save() error {
143+
144+
var hashFile XlsxHashFile
145+
hashFile.CRC32Map = make(map[string]uint32)
146+
147+
for _, f := range self.z.File {
148+
hashFile.CRC32Map[f.Name] = f.CRC32
149+
}
150+
151+
writeJsonFile(self.hashFileName(), &hashFile)
152+
153+
var newFile XlsxFileCache
154+
newFile.Name = self.name
155+
156+
for _, sheet := range self.originFile.Sheets {
157+
158+
var newSheet XlsxSheet
159+
newSheet.Name = sheet.Name
160+
161+
newSheet.Cells = make([][]string, 0, len(sheet.Rows))
162+
163+
for _, row := range sheet.Rows {
164+
165+
var rowData = make([]string, 0, len(row.Cells))
166+
for _, c := range row.Cells {
167+
rowData = append(rowData, c.Value)
168+
}
169+
170+
newSheet.Cells = append(newSheet.Cells, rowData)
171+
}
172+
173+
newFile.Sheets = append(newFile.Sheets, &newSheet)
174+
}
175+
176+
writeJsonFile(self.cacheFileName(), &newFile)
177+
178+
return nil
179+
}

v2/file.go

+27-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v2
22

33
import (
4+
"github.com/davyxu/tabtoy/util"
45
"strings"
56

67
"github.com/davyxu/tabtoy/v2/i18n"
@@ -155,23 +156,43 @@ func isTypeSheet(name string) bool {
155156
return strings.TrimSpace(name) == model.TypeSheetName
156157
}
157158

158-
func NewFile(filename string) *File {
159+
func NewFile(filename string, cacheDir string) (f *File, fromCache bool) {
159160

160161
self := &File{
161162
valueRepByKey: make(map[valueRepeatData]bool),
162163
LocalFD: model.NewFileDescriptor(),
163164
FileName: filename,
164165
}
165166

167+
if cacheDir != "" {
168+
cache := util.NewTableCache(filename, cacheDir)
169+
170+
if err := cache.Open(); err != nil {
171+
log.Errorf("%s, %v", i18n.String(i18n.System_OpenReadXlsxFailed), err.Error())
172+
return nil, false
173+
}
174+
175+
if cfile, err := cache.Load(); err != nil {
176+
log.Errorln(err.Error())
177+
log.Errorf("%s, %v", i18n.String(i18n.System_OpenReadXlsxFailed), err.Error())
178+
return nil, false
179+
} else {
180+
self.coreFile = cfile
181+
182+
if !cache.UseCache() {
183+
cache.Save()
184+
}
185+
186+
return self, cache.UseCache()
187+
}
188+
}
189+
166190
var err error
167191
self.coreFile, err = xlsx.OpenFile(filename)
168-
169192
if err != nil {
170-
log.Errorln(err.Error())
171193
log.Errorf("%s, %v", i18n.String(i18n.System_OpenReadXlsxFailed), err.Error())
172-
173-
return nil
194+
return nil, false
174195
}
175196

176-
return self
197+
return self, false
177198
}

v2/filecache.go

+30-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package v2
33
import (
44
"github.com/davyxu/tabtoy/v2/i18n"
55
"github.com/davyxu/tabtoy/v2/printer"
6+
"os"
67
"path/filepath"
78
"strings"
89
"sync"
@@ -33,20 +34,36 @@ func cacheFile(g *printer.Globals) (fileObjByName map[string]*File) {
3334

3435
filelist := getFileList(g)
3536

37+
var cachedir string
38+
if g.UseCache {
39+
cachedir = g.CacheDir
40+
41+
os.Mkdir(g.CacheDir, 0666)
42+
}
43+
44+
writeOK := func(xlsxFileName string) {
45+
file, fromCache := NewFile(xlsxFileName, cachedir)
46+
47+
fileObjByNameGuard.Lock()
48+
fileObjByName[xlsxFileName] = file
49+
fileObjByNameGuard.Unlock()
50+
if fromCache {
51+
log.Infof("%s [Cache]", filepath.Base(xlsxFileName))
52+
} else {
53+
log.Infof("%s", filepath.Base(xlsxFileName))
54+
}
55+
56+
}
57+
58+
// 这里加速效果良好, 默认开启
3659
var task sync.WaitGroup
3760
task.Add(len(filelist))
3861

3962
for _, filename := range filelist {
4063

4164
go func(xlsxFileName string) {
4265

43-
log.Infoln(filepath.Base(xlsxFileName))
44-
file := NewFile(xlsxFileName)
45-
46-
fileObjByNameGuard.Lock()
47-
fileObjByName[xlsxFileName] = file
48-
fileObjByNameGuard.Unlock()
49-
66+
writeOK(xlsxFileName)
5067
task.Done()
5168

5269
}(filename)
@@ -55,5 +72,11 @@ func cacheFile(g *printer.Globals) (fileObjByName map[string]*File) {
5572

5673
task.Wait()
5774

75+
// 调试用
76+
//for _, filename := range filelist {
77+
//
78+
// writeOK(filename)
79+
//}
80+
5881
return
5982
}

v2/printer/globals.go

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type Globals struct {
1515
Version string
1616
InputFileList []interface{}
1717
ParaMode bool
18+
CacheDir string
19+
UseCache bool
1820
ProtoVersion int
1921
LuaEnumIntValue bool
2022
LuaTabHeader string

v2tov3/model/globals.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type Globals struct {
2525

2626
func (self *Globals) AddTableByFile(tableFileName, tableName string, inputFile *xlsx.File) {
2727

28-
file := helper.NewXlsxFile()
28+
file := helper.NewXlsxFile("")
2929

3030
file.(interface {
3131
FromXFile(file *xlsx.File)

0 commit comments

Comments
 (0)