diff --git a/export.go b/export.go index a904827..58a498e 100644 --- a/export.go +++ b/export.go @@ -10,6 +10,7 @@ const ( ExportFormatExcel ExportFormat = "XLSX" ExportFormatCsv ExportFormat = "CSV" ExportFormatCsvZip ExportFormat = "CSV+ZIP" + ExportFormatCsvZst ExportFormat = "CSV+ZSTD" ) // Exporter - интерфейс экспорта @@ -25,6 +26,8 @@ func (e ExportFormat) GetExporter(encoding Encoding) (Exporter, error) { return &CsvExporter{Encoding: encoding}, nil case ExportFormatCsvZip: return &CsvZipExporter{Encoding: encoding}, nil + case ExportFormatCsvZst: + return &CsvZstExporter{Encoding: encoding}, nil } return nil, fmt.Errorf("unknown format: %s", string(e)) diff --git a/export_csvzst.go b/export_csvzst.go new file mode 100644 index 0000000..cdd469a --- /dev/null +++ b/export_csvzst.go @@ -0,0 +1,81 @@ +package main + +import ( + "bufio" + "encoding/csv" + "os" + + "github.com/klauspost/compress/zstd" +) + +// Экспорт в CSV, сжатый в ZIP-архив +type CsvZstExporter struct { + Encoding Encoding +} + +func (c *CsvZstExporter) FileExt() string { + return ".csv.zst" +} + +func (c *CsvZstExporter) Convert(filePath string, rows chan []any) error { + f, err := os.Create(filePath + c.FileExt()) + if err != nil { + return err + } + + buf := bufio.NewWriterSize(f, 4*1024*1024) + + z, err := zstd.NewWriter(buf) + if err != nil { + f.Close() + return err + } + + enc, err := c.Encoding.Encoder() + if err != nil { + return err + } + + w := csv.NewWriter(enc.Writer(z)) + w.Comma = ';' + + rowNum := 0 + for row := range rows { + rowNum++ + rowsStr := make([]string, len(row)) + for i := range row { + rowsStr[i] = toCsvField(row[i]) + } + + err = w.Write(rowsStr) + if err != nil { + f.Close() + return err + } + } + + w.Flush() + if err = w.Error(); err != nil { + f.Close() + return err + } + + err = z.Close() + if err != nil { + f.Close() + return err + } + + err = buf.Flush() + if err != nil { + f.Close() + return err + } + + err = f.Close() + if err != nil { + return err + } + + return nil +} diff --git a/go.mod b/go.mod index da376c2..3abcd8e 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( ) require ( + github.com/klauspost/compress v1.17.7 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/msoleps v1.0.3 // indirect diff --git a/go.sum b/go.sum index 39eb6ff..63fa4cd 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/ui_main_form.go b/ui_main_form.go index 4353a8c..851b0e3 100644 --- a/ui_main_form.go +++ b/ui_main_form.go @@ -246,7 +246,7 @@ func (f *TMainForm) OnFormCreate(sender vcl.IObject) { // ------------------------------------------------------------------------- // File formats ------------------------------------------------------------ - for _, v := range []string{string(ExportFormatExcel), string(ExportFormatCsv), string(ExportFormatCsvZip)} { + for _, v := range []string{string(ExportFormatExcel), string(ExportFormatCsv), string(ExportFormatCsvZip), string(ExportFormatCsvZst)} { f.ExportFormatComboBox.Items().Add(v) } f.ExportFormatComboBox.SetItemIndex(0) @@ -322,7 +322,7 @@ func (f *TMainForm) OnLaunchButtonClick(sender vcl.IObject) { } func (f *TMainForm) OnExportFormatComboBoxChange(sender vcl.IObject) { - if slices.Contains([]string{string(ExportFormatCsv), string(ExportFormatCsvZip)}, f.ExportFormatComboBox.Text()) { + if slices.Contains([]string{string(ExportFormatCsv), string(ExportFormatCsvZip), string(ExportFormatCsvZst)}, f.ExportFormatComboBox.Text()) { f.CharsetComboBox.SetEnabled(true) } else { f.CharsetComboBox.SetEnabled(false)