Fix issue #2 change project to object-oriented style and update readme file.

This commit is contained in:
Ri Xu 2016-09-05 16:37:15 +08:00
parent 956a4627d1
commit 50863294f9
7 changed files with 144 additions and 131 deletions

View File

@ -11,7 +11,7 @@
## Introduction
Excelize is a library written in pure Golang and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Office Excel 2007 and later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/Luxurioust/excelize).
Excelize is a library written in pure Golang and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Office Excel 2007 and later. Support save file without losing original charts of XLSX. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/Luxurioust/excelize).
## Basic Usage
@ -35,11 +35,11 @@ import (
func main() {
xlsx := excelize.CreateFile()
xlsx = excelize.NewSheet(xlsx, 2, "Sheet2")
xlsx = excelize.NewSheet(xlsx, 3, "Sheet3")
xlsx = excelize.SetCellInt(xlsx, "Sheet2", "A23", 10)
xlsx = excelize.SetCellStr(xlsx, "Sheet3", "B20", "Hello")
err := excelize.Save(xlsx, "/home/Workbook.xlsx")
xlsx.NewSheet(2, "Sheet2")
xlsx.NewSheet(3, "Sheet3")
xlsx.SetCellInt("Sheet2", "A23", 10)
xlsx.SetCellStr("Sheet3", "B20", "Hello")
err := xlsx.WriteTo("/home/Workbook.xlsx")
if err != nil {
fmt.Println(err)
}
@ -59,20 +59,17 @@ import (
)
func main() {
xlsx, err := excelize.OpenFile("/home/Workbook.xlsx")
xlsx := excelize.OpenFile("/home/Workbook.xlsx")
xlsx.SetCellInt("Sheet2", "B2", 100)
xlsx.SetCellStr("Sheet2", "C11", "Hello")
xlsx.NewSheet(3, "TestSheet")
xlsx.SetCellInt("Sheet3", "A23", 10)
xlsx.SetCellStr("Sheet3", "b230", "World")
xlsx.SetActiveSheet(2)
err := xlsx.Save()
if err != nil {
fmt.Println(err)
}
xlsx = excelize.SetCellInt(xlsx, "Sheet2", "B2", 100)
xlsx = excelize.SetCellStr(xlsx, "Sheet2", "C11", "Hello")
xlsx = excelize.NewSheet(xlsx, 3, "TestSheet")
xlsx = excelize.SetCellInt(xlsx, "Sheet3", "A23", 10)
xlsx = excelize.SetCellStr(xlsx, "Sheet3", "b230", "World")
xlsx = excelize.SetActiveSheet(xlsx, 2)
err = excelize.Save(xlsx, "/home/Workbook.xlsx")
if err != nil {
fmt.Println(err)
}
}
```
@ -87,11 +84,8 @@ import (
)
func main() {
xlsx, err := excelize.OpenFile("/home/Workbook.xlsx")
if err != nil {
fmt.Println(err)
}
cell := excelize.GetCellValue(file, "Sheet2", "D11")
xlsx := excelize.OpenFile("/home/Workbook.xlsx")
cell := xlsx.GetCellValue("Sheet2", "D11")
fmt.Println(cell)
}
```

View File

@ -7,13 +7,13 @@ import (
)
// GetCellValue provide function get value from cell by given sheet index and axis in XLSX file
func GetCellValue(file map[string]string, sheet string, axis string) string {
func (f *File) GetCellValue(sheet string, axis string) string {
axis = strings.ToUpper(axis)
var xlsx xlsxWorksheet
row := getRowIndex(axis)
xAxis := row - 1
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
xml.Unmarshal([]byte(readXML(file, name)), &xlsx)
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
rows := len(xlsx.SheetData.Row)
if rows <= xAxis {
return ``
@ -26,7 +26,7 @@ func GetCellValue(file map[string]string, sheet string, axis string) string {
shardStrings := xlsxSST{}
xlsxSI := 0
xlsxSI, _ = strconv.Atoi(v.V)
xml.Unmarshal([]byte(readXML(file, `xl/sharedStrings.xml`)), &shardStrings)
xml.Unmarshal([]byte(f.readXML(`xl/sharedStrings.xml`)), &shardStrings)
return shardStrings.SI[xlsxSI].T
case "str":
return v.V

View File

@ -9,26 +9,27 @@ import (
"strings"
)
// FileList define a populated xlsx.File struct.
type FileList struct {
Key string
Value string
// File define a populated xlsx.File struct.
type File struct {
XLSX map[string]string
Path string
}
// OpenFile take the name of an XLSX file and returns a populated
// xlsx.File struct for it.
func OpenFile(filename string) (file map[string]string, err error) {
func OpenFile(filename string) *File {
var f *zip.ReadCloser
f, err = zip.OpenReader(filename)
if err != nil {
return nil, err
file := make(map[string]string)
f, _ = zip.OpenReader(filename)
file, _ = ReadZip(f)
return &File{
XLSX: file,
Path: filename,
}
file, err = ReadZip(f)
return
}
// SetCellInt provide function to set int type value of a cell
func SetCellInt(file map[string]string, sheet string, axis string, value int) map[string]string {
func (f *File) SetCellInt(sheet string, axis string, value int) {
axis = strings.ToUpper(axis)
var xlsx xlsxWorksheet
col := getColIndex(axis)
@ -37,7 +38,7 @@ func SetCellInt(file map[string]string, sheet string, axis string, value int) ma
yAxis := titleToNumber(col)
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
xml.Unmarshal([]byte(readXML(file, name)), &xlsx)
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
rows := xAxis + 1
cell := yAxis + 1
@ -53,12 +54,11 @@ func SetCellInt(file map[string]string, sheet string, axis string, value int) ma
if err != nil {
fmt.Println(err)
}
saveFileList(file, name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
return file
f.saveFileList(name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
}
// SetCellStr provide function to set string type value of a cell
func SetCellStr(file map[string]string, sheet string, axis string, value string) map[string]string {
func (f *File) SetCellStr(sheet string, axis string, value string) {
axis = strings.ToUpper(axis)
var xlsx xlsxWorksheet
col := getColIndex(axis)
@ -67,7 +67,7 @@ func SetCellStr(file map[string]string, sheet string, axis string, value string)
yAxis := titleToNumber(col)
name := `xl/worksheets/` + strings.ToLower(sheet) + `.xml`
xml.Unmarshal([]byte(readXML(file, name)), &xlsx)
xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
rows := xAxis + 1
cell := yAxis + 1
@ -83,8 +83,7 @@ func SetCellStr(file map[string]string, sheet string, axis string, value string)
if err != nil {
fmt.Println(err)
}
saveFileList(file, name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
return file
f.saveFileList(name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
}
// Completion column element tags of XML in a sheet

View File

@ -7,57 +7,51 @@ import (
func TestExcelize(t *testing.T) {
// Test update a XLSX file
file, err := OpenFile("./test/Workbook1.xlsx")
if err != nil {
t.Log(err)
}
file = SetCellInt(file, "SHEET2", "B2", 100)
file = SetCellStr(file, "SHEET2", "C11", "Knowns")
file = NewSheet(file, 3, "TestSheet")
file = SetCellInt(file, "Sheet3", "A23", 10)
file = SetCellStr(file, "SHEET3", "b230", "10")
file = SetActiveSheet(file, 2)
if err != nil {
t.Log(err)
}
file := OpenFile("./test/Workbook1.xlsx")
file.SetCellInt("SHEET2", "B2", 100)
file.SetCellStr("SHEET2", "C11", "Knowns")
file.NewSheet(3, "TestSheet")
file.SetCellInt("Sheet3", "A23", 10)
file.SetCellStr("SHEET3", "b230", "10")
file.SetCellStr("SHEET10", "b230", "10")
file.SetActiveSheet(2)
for i := 1; i <= 300; i++ {
file = SetCellStr(file, "SHEET3", "c"+strconv.Itoa(i), strconv.Itoa(i))
file.SetCellStr("SHEET3", "c"+strconv.Itoa(i), strconv.Itoa(i))
}
err = Save(file, "./test/Workbook_2.xlsx")
err := file.Save()
if err != nil {
t.Log(err)
}
// Test save to not exist directory
err = Save(file, "")
// Test write file to given path
err = file.WriteTo("./test/Workbook_2.xlsx")
if err != nil {
t.Log(err)
}
// Test write file to not exist directory
err = file.WriteTo("")
if err != nil {
t.Log(err)
}
// Test create a XLSX file
file2 := CreateFile()
file2 = NewSheet(file2, 2, "TestSheet2")
file2 = NewSheet(file2, 3, "TestSheet3")
file2 = SetCellInt(file2, "Sheet2", "A23", 10)
file2 = SetCellStr(file2, "SHEET1", "B20", "10")
file2 = SetActiveSheet(file2, 0)
err = Save(file2, "./test/Workbook_3.xlsx")
file2.NewSheet(2, "XLSXSheet2")
file2.NewSheet(3, "XLSXSheet3")
file2.SetCellInt("Sheet2", "A23", 56)
file2.SetCellStr("SHEET1", "B20", "42")
file2.SetActiveSheet(0)
err = file2.WriteTo("./test/Workbook_3.xlsx")
if err != nil {
t.Log(err)
}
// Test read cell value
file, err = OpenFile("./test/Workbook1.xlsx")
if err != nil {
t.Log(err)
}
// Test given illegal rows number
GetCellValue(file, "Sheet2", "a-1")
// Test given lowercase column number
GetCellValue(file, "Sheet2", "a5")
GetCellValue(file, "Sheet2", "C11")
GetCellValue(file, "Sheet2", "D11")
GetCellValue(file, "Sheet2", "D12")
// Test given axis large than exists row
GetCellValue(file, "Sheet2", "E13")
// Test read cell value with given illegal rows number
file.GetCellValue("Sheet2", "a-1")
// Test read cell value with given lowercase column number
file.GetCellValue("Sheet2", "a5")
file.GetCellValue("Sheet2", "C11")
file.GetCellValue("Sheet2", "D11")
file.GetCellValue("Sheet2", "D12")
// Test read cell value with given axis large than exists row
file.GetCellValue("Sheet2", "E13")
}

60
file.go
View File

@ -9,25 +9,27 @@ import (
// CreateFile provide function to create new file by default template
// For example:
// xlsx := CreateFile()
func CreateFile() map[string]string {
func CreateFile() *File {
file := make(map[string]string)
file = saveFileList(file, `_rels/.rels`, templateRels)
file = saveFileList(file, `docProps/app.xml`, templateDocpropsApp)
file = saveFileList(file, `docProps/core.xml`, templateDocpropsCore)
file = saveFileList(file, `xl/_rels/workbook.xml.rels`, templateWorkbookRels)
file = saveFileList(file, `xl/theme/theme1.xml`, templateTheme)
file = saveFileList(file, `xl/worksheets/sheet1.xml`, templateSheet)
file = saveFileList(file, `xl/styles.xml`, templateStyles)
file = saveFileList(file, `xl/workbook.xml`, templateWorkbook)
file = saveFileList(file, `[Content_Types].xml`, templateContentTypes)
return file
file[`_rels/.rels`] = templateRels
file[`docProps/app.xml`] = templateDocpropsApp
file[`docProps/core.xml`] = templateDocpropsCore
file[`xl/_rels/workbook.xml.rels`] = templateWorkbookRels
file[`xl/theme/theme1.xml`] = templateTheme
file[`xl/worksheets/sheet1.xml`] = templateSheet
file[`xl/styles.xml`] = templateStyles
file[`xl/workbook.xml`] = templateWorkbook
file[`[Content_Types].xml`] = templateContentTypes
return &File{
XLSX: file,
}
}
// Save after create or update to an xlsx file at the provided path.
func Save(files map[string]string, name string) error {
// Save provide function override the xlsx file with origin path.
func (f *File) Save() error {
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
for path, content := range files {
for path, content := range f.XLSX {
f, err := w.Create(path)
if err != nil {
return err
@ -41,10 +43,36 @@ func Save(files map[string]string, name string) error {
if err != nil {
return err
}
f, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
file, err := os.OpenFile(f.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
return err
}
buf.WriteTo(f)
buf.WriteTo(file)
return err
}
// WriteTo provide function create or update to an xlsx file at the provided path.
func (f *File) WriteTo(name string) error {
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
for path, content := range f.XLSX {
f, err := w.Create(path)
if err != nil {
return err
}
_, err = f.Write([]byte(content))
if err != nil {
return err
}
}
err := w.Close()
if err != nil {
return err
}
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
return err
}
buf.WriteTo(file)
return err
}

9
lib.go
View File

@ -30,17 +30,16 @@ func ReadZipReader(r *zip.Reader) (map[string]string, error) {
}
// Read XML content as string and replace drawing property in XML namespace of sheet
func readXML(files map[string]string, name string) string {
if content, ok := files[name]; ok {
func (f *File) readXML(name string) string {
if content, ok := f.XLSX[name]; ok {
return strings.Replace(content, "<drawing r:id=", "<drawing rid=", -1)
}
return ``
}
// Update given file content in file list of XLSX
func saveFileList(files map[string]string, name string, content string) map[string]string {
files[name] = XMLHeader + content
return files
func (f *File) saveFileList(name string, content string) {
f.XLSX[name] = XMLHeader + content
}
// Read file content as string in a archive file

View File

@ -11,24 +11,23 @@ import (
// NewSheet provice function to greate a new sheet by given index, when
// creating a new XLSX file, the default sheet will be create, when you
// create a new file, you need to ensure that the index is continuous.
func NewSheet(file map[string]string, index int, name string) map[string]string {
func (f *File) NewSheet(index int, name string) {
// Update docProps/app.xml
file = setAppXML(file)
f.setAppXML()
// Update [Content_Types].xml
file = setContentTypes(file, index)
f.setContentTypes(index)
// Create new sheet /xl/worksheets/sheet%d.xml
file = setSheet(file, index)
f.setSheet(index)
// Update xl/_rels/workbook.xml.rels
file = addXlsxWorkbookRels(file, index)
f.addXlsxWorkbookRels(index)
// Update xl/workbook.xml
file = setWorkbook(file, index, name)
return file
f.setWorkbook(index, name)
}
// Read and update property of contents type of XLSX
func setContentTypes(file map[string]string, index int) map[string]string {
func (f *File) setContentTypes(index int) {
var content xlsxTypes
xml.Unmarshal([]byte(readXML(file, `[Content_Types].xml`)), &content)
xml.Unmarshal([]byte(f.readXML(`[Content_Types].xml`)), &content)
content.Overrides = append(content.Overrides, xlsxOverride{
PartName: `/xl/worksheets/sheet` + strconv.Itoa(index) + `.xml`,
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
@ -37,11 +36,11 @@ func setContentTypes(file map[string]string, index int) map[string]string {
if err != nil {
fmt.Println(err)
}
return saveFileList(file, `[Content_Types].xml`, string(output))
f.saveFileList(`[Content_Types].xml`, string(output))
}
// Update sheet property by given index
func setSheet(file map[string]string, index int) map[string]string {
func (f *File) setSheet(index int) {
var xlsx xlsxWorksheet
xlsx.Dimension.Ref = "A1"
xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{
@ -52,15 +51,15 @@ func setSheet(file map[string]string, index int) map[string]string {
fmt.Println(err)
}
path := `xl/worksheets/sheet` + strconv.Itoa(index) + `.xml`
return saveFileList(file, path, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
f.saveFileList(path, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
}
// Update workbook property of XLSX
func setWorkbook(file map[string]string, index int, name string) map[string]string {
func (f *File) setWorkbook(index int, name string) {
var content xlsxWorkbook
xml.Unmarshal([]byte(readXML(file, `xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
rels := readXlsxWorkbookRels(file)
rels := f.readXlsxWorkbookRels()
rID := len(rels.Relationships)
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
Name: name,
@ -71,19 +70,19 @@ func setWorkbook(file map[string]string, index int, name string) map[string]stri
if err != nil {
fmt.Println(err)
}
return saveFileList(file, `xl/workbook.xml`, replaceRelationshipsNameSpace(string(output)))
f.saveFileList(`xl/workbook.xml`, replaceRelationshipsNameSpace(string(output)))
}
// Read and unmarshal workbook relationships of XLSX
func readXlsxWorkbookRels(file map[string]string) xlsxWorkbookRels {
func (f *File) readXlsxWorkbookRels() xlsxWorkbookRels {
var content xlsxWorkbookRels
xml.Unmarshal([]byte(readXML(file, `xl/_rels/workbook.xml.rels`)), &content)
xml.Unmarshal([]byte(f.readXML(`xl/_rels/workbook.xml.rels`)), &content)
return content
}
// Update workbook relationships property of XLSX
func addXlsxWorkbookRels(file map[string]string, sheet int) map[string]string {
content := readXlsxWorkbookRels(file)
func (f *File) addXlsxWorkbookRels(sheet int) {
content := f.readXlsxWorkbookRels()
rID := len(content.Relationships) + 1
ID := bytes.Buffer{}
ID.WriteString("rId")
@ -101,12 +100,12 @@ func addXlsxWorkbookRels(file map[string]string, sheet int) map[string]string {
if err != nil {
fmt.Println(err)
}
return saveFileList(file, `xl/_rels/workbook.xml.rels`, string(output))
f.saveFileList(`xl/_rels/workbook.xml.rels`, string(output))
}
// Update docProps/app.xml file of XML
func setAppXML(file map[string]string) map[string]string {
return saveFileList(file, `docProps/app.xml`, templateDocpropsApp)
func (f *File) setAppXML() {
f.saveFileList(`docProps/app.xml`, templateDocpropsApp)
}
// Some tools that read XLSX files have very strict requirements about
@ -134,13 +133,13 @@ func replaceRelationshipsID(workbookMarshal string) string {
}
// SetActiveSheet provide function to set default active sheet of XLSX by given index
func SetActiveSheet(file map[string]string, index int) map[string]string {
func (f *File) SetActiveSheet(index int) {
var content xlsxWorkbook
if index < 1 {
index = 1
}
index--
xml.Unmarshal([]byte(readXML(file, `xl/workbook.xml`)), &content)
xml.Unmarshal([]byte(f.readXML(`xl/workbook.xml`)), &content)
if len(content.BookViews.WorkBookView) > 0 {
content.BookViews.WorkBookView[0].ActiveTab = index
} else {
@ -153,7 +152,7 @@ func SetActiveSheet(file map[string]string, index int) map[string]string {
if err != nil {
fmt.Println(err)
}
file = saveFileList(file, `xl/workbook.xml`, workBookCompatibility(replaceRelationshipsNameSpace(string(output))))
f.saveFileList(`xl/workbook.xml`, workBookCompatibility(replaceRelationshipsNameSpace(string(output))))
index++
buffer := bytes.Buffer{}
for i := 0; i < sheets; i++ {
@ -162,7 +161,7 @@ func SetActiveSheet(file map[string]string, index int) map[string]string {
buffer.WriteString(`xl/worksheets/sheet`)
buffer.WriteString(strconv.Itoa(sheetIndex))
buffer.WriteString(`.xml`)
xml.Unmarshal([]byte(readXML(file, buffer.String())), &xlsx)
xml.Unmarshal([]byte(f.readXML(buffer.String())), &xlsx)
if index == sheetIndex {
if len(xlsx.SheetViews.SheetView) > 0 {
xlsx.SheetViews.SheetView[0].TabSelected = true
@ -180,10 +179,10 @@ func SetActiveSheet(file map[string]string, index int) map[string]string {
if err != nil {
fmt.Println(err)
}
file = saveFileList(file, buffer.String(), replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(sheet))))
f.saveFileList(buffer.String(), replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(sheet))))
buffer.Reset()
}
return file
return
}
// Replace xl/workbook.xml XML tags to self-closing for compatible Office Excel 2007