- Improved performance when reading large files, call Token to read tokens one by one instead Unmarshal. Related issue #20 ;

- Fix go test typo;
- Update README
This commit is contained in:
Ri Xu 2017-02-12 19:03:24 +08:00
parent 53564cbe57
commit 0833a9d5da
7 changed files with 94 additions and 28 deletions

View File

@ -110,13 +110,18 @@ func main() {
} }
// Insert a picture. // Insert a picture.
err = xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.gif", 0, 0, 1, 1) err = xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.gif", 0, 0, 1, 1)
if err != nil {
fmt.Println(err)
}
// Insert a picture to sheet with scaling. // Insert a picture to sheet with scaling.
err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image2.jpg", 0, 0, 0.5, 0.5) err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image2.jpg", 0, 0, 0.5, 0.5)
if err != nil {
fmt.Println(err)
}
// Insert a picture offset in the cell. // Insert a picture offset in the cell.
err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.png", 15, 10, 1, 1) err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.png", 15, 10, 1, 1)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1)
} }
// Save the xlsx file with the origin path. // Save the xlsx file with the origin path.
err = xlsx.Save() err = xlsx.Save()

24
cell.go
View File

@ -106,6 +106,14 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ { for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@ -143,6 +151,14 @@ func (f *File) SetCellHyperLink(sheet, axis, link string) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ { for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@ -205,6 +221,14 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
mergeCell := xlsxMergeCell{} mergeCell := xlsxMergeCell{}
// Correct the coordinate area, such correct C1:B3 to B1:C3. // Correct the coordinate area, such correct C1:B3 to B1:C3.

View File

@ -10,6 +10,7 @@ import (
// File define a populated XLSX file struct. // File define a populated XLSX file struct.
type File struct { type File struct {
checked map[string]bool
XLSX map[string]string XLSX map[string]string
Path string Path string
SheetCount int SheetCount int
@ -21,6 +22,7 @@ func OpenFile(filename string) (*File, error) {
var f *zip.ReadCloser var f *zip.ReadCloser
var err error var err error
file := make(map[string]string) file := make(map[string]string)
c := make(map[string]bool)
sheetCount := 0 sheetCount := 0
f, err = zip.OpenReader(filename) f, err = zip.OpenReader(filename)
if err != nil { if err != nil {
@ -28,6 +30,7 @@ func OpenFile(filename string) (*File, error) {
} }
file, sheetCount, _ = ReadZip(f) file, sheetCount, _ = ReadZip(f)
return &File{ return &File{
checked: c,
XLSX: file, XLSX: file,
Path: filename, Path: filename,
SheetCount: sheetCount, SheetCount: sheetCount,
@ -66,6 +69,15 @@ func (f *File) SetCellInt(sheet string, axis string, value int) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ { for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@ -98,6 +110,14 @@ func (f *File) SetCellStr(sheet string, axis string, value string) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ { for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@ -133,6 +153,14 @@ func (f *File) SetCellDefault(sheet string, axis string, value string) {
var xlsx xlsxWorksheet var xlsx xlsxWorksheet
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
xml.Unmarshal([]byte(f.readXML(name)), &xlsx) xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
if f.checked == nil {
f.checked = make(map[string]bool)
}
ok := f.checked[name]
if !ok {
xlsx = checkRow(xlsx)
f.checked[name] = true
}
if xlsx.MergeCells != nil { if xlsx.MergeCells != nil {
for i := 0; i < len(xlsx.MergeCells.Cells); i++ { for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) { if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {

View File

@ -236,7 +236,7 @@ func TestSetSheetBackground(t *testing.T) {
} }
} }
func TestSMergeCell(t *testing.T) { func TestMergeCell(t *testing.T) {
xlsx, err := OpenFile("./test/Workbook1.xlsx") xlsx, err := OpenFile("./test/Workbook1.xlsx")
if err != nil { if err != nil {
t.Log(err) t.Log(err)

6
lib.go
View File

@ -3,7 +3,6 @@ package excelize
import ( import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"encoding/xml"
"io" "io"
"log" "log"
"math" "math"
@ -26,11 +25,6 @@ func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
fileList[v.Name] = readFile(v) fileList[v.Name] = readFile(v)
if len(v.Name) > 18 { if len(v.Name) > 18 {
if v.Name[0:19] == "xl/worksheets/sheet" { if v.Name[0:19] == "xl/worksheets/sheet" {
var xlsx xlsxWorksheet
xml.Unmarshal([]byte(fileList[v.Name]), &xlsx)
xlsx = checkRow(xlsx)
output, _ := xml.Marshal(xlsx)
fileList[v.Name] = replaceWorkSheetsRelationshipsNameSpace(string(output))
worksheets++ worksheets++
} }
} }

View File

@ -33,13 +33,18 @@ import (
// xlsx := excelize.CreateFile() // xlsx := excelize.CreateFile()
// // Insert a picture. // // Insert a picture.
// err := xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.jpg", 0, 0, 1, 1) // err := xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.jpg", 0, 0, 1, 1)
// if err != nil {
// fmt.Println(err)
// }
// // Insert a picture to sheet with scaling. // // Insert a picture to sheet with scaling.
// err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image1.png", 0, 0, 0.5, 0.5) // err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image1.png", 0, 0, 0.5, 0.5)
// if err != nil {
// fmt.Println(err)
// }
// // Insert a picture offset in the cell. // // Insert a picture offset in the cell.
// err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.gif", 15, 10, 1, 1) // err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.gif", 15, 10, 1, 1)
// if err != nil { // if err != nil {
// fmt.Println(err) // fmt.Println(err)
// os.Exit(1)
// } // }
// err = xlsx.WriteTo("/tmp/Workbook.xlsx") // err = xlsx.WriteTo("/tmp/Workbook.xlsx")
// if err != nil { // if err != nil {

48
rows.go
View File

@ -17,23 +17,37 @@ import (
// } // }
// //
func (f *File) GetRows(sheet string) [][]string { func (f *File) GetRows(sheet string) [][]string {
xlsx := xlsxWorksheet{} rows := [][]string{}
r := [][]string{}
name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml" name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
err := xml.Unmarshal([]byte(f.readXML(name)), &xlsx) decoder := xml.NewDecoder(strings.NewReader(f.readXML(name)))
d, err := readXMLSST(f)
if err != nil { if err != nil {
return r return rows
} }
rows := xlsx.SheetData.Row var inElement string
for _, row := range rows { var row []string
c := []string{} for {
for _, colCell := range row.C { token, _ := decoder.Token()
val, _ := colCell.getValueFrom(f) if token == nil {
c = append(c, val) break
} }
r = append(r, c) switch startElement := token.(type) {
case xml.StartElement:
inElement = startElement.Name.Local
if inElement == "row" {
var r xlsxRow
decoder.DecodeElement(&r, &startElement)
for _, colCell := range r.C {
val, _ := colCell.getValueFrom(f, d)
row = append(row, val)
} }
return r rows = append(rows, row)
row = row[:0]
}
default:
}
}
return rows
} }
// SetRowHeight provides a function to set the height of a single row. // SetRowHeight provides a function to set the height of a single row.
@ -65,23 +79,19 @@ func (f *File) SetRowHeight(sheet string, rowIndex int, height float64) {
} }
// readXMLSST read xmlSST simple function. // readXMLSST read xmlSST simple function.
func readXMLSST(f *File) (xlsxSST, error) { func readXMLSST(f *File) (*xlsxSST, error) {
shardStrings := xlsxSST{} shardStrings := xlsxSST{}
err := xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings) err := xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
return shardStrings, err return &shardStrings, err
} }
// getValueFrom return a value from a column/row cell, this function is inteded // getValueFrom return a value from a column/row cell, this function is inteded
// to be used with for range on rows an argument with the xlsx opened file. // to be used with for range on rows an argument with the xlsx opened file.
func (xlsx *xlsxC) getValueFrom(f *File) (string, error) { func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
switch xlsx.T { switch xlsx.T {
case "s": case "s":
xlsxSI := 0 xlsxSI := 0
xlsxSI, _ = strconv.Atoi(xlsx.V) xlsxSI, _ = strconv.Atoi(xlsx.V)
d, err := readXMLSST(f)
if err != nil {
return "", err
}
if len(d.SI[xlsxSI].R) > 0 { if len(d.SI[xlsxSI].R) > 0 {
value := "" value := ""
for _, v := range d.SI[xlsxSI].R { for _, v := range d.SI[xlsxSI].R {