support case-sensitive doc parts to improve compatibility
This commit is contained in:
parent
f7bd0729c6
commit
49257c5918
12
README.md
12
README.md
|
@ -29,9 +29,9 @@ go get github.com/360EntSecGroup-Skylar/excelize
|
||||||
go get github.com/360EntSecGroup-Skylar/excelize/v2
|
go get github.com/360EntSecGroup-Skylar/excelize/v2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create XLSX file
|
### Create spreadsheet
|
||||||
|
|
||||||
Here is a minimal example usage that will create XLSX file.
|
Here is a minimal example usage that will create spreadsheet file.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@ -58,9 +58,9 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reading XLSX file
|
### Reading spreadsheet
|
||||||
|
|
||||||
The following constitutes the bare to read a XLSX document.
|
The following constitutes the bare to read a spreadsheet document.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@ -95,7 +95,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add chart to XLSX file
|
### Add chart to spreadsheet file
|
||||||
|
|
||||||
With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
|
With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add picture to XLSX file
|
### Add picture to spreadsheet file
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
11
excelize.go
11
excelize.go
|
@ -220,16 +220,25 @@ func checkSheet(xlsx *xlsxWorksheet) {
|
||||||
// addRels provides a function to add relationships by given XML path,
|
// addRels provides a function to add relationships by given XML path,
|
||||||
// relationship type, target and target mode.
|
// relationship type, target and target mode.
|
||||||
func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
func (f *File) addRels(relPath, relType, target, targetMode string) int {
|
||||||
|
var uniqPart = map[string]string{
|
||||||
|
SourceRelationshipSharedStrings: "/xl/sharedStrings.xml",
|
||||||
|
}
|
||||||
rels := f.relsReader(relPath)
|
rels := f.relsReader(relPath)
|
||||||
if rels == nil {
|
if rels == nil {
|
||||||
rels = &xlsxRelationships{}
|
rels = &xlsxRelationships{}
|
||||||
}
|
}
|
||||||
var rID int
|
var rID int
|
||||||
for _, rel := range rels.Relationships {
|
for idx, rel := range rels.Relationships {
|
||||||
ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
|
ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId"))
|
||||||
if ID > rID {
|
if ID > rID {
|
||||||
rID = ID
|
rID = ID
|
||||||
}
|
}
|
||||||
|
if relType == rel.Type {
|
||||||
|
if partName, ok := uniqPart[rel.Type]; ok {
|
||||||
|
rels.Relationships[idx].Target = partName
|
||||||
|
return rID
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rID++
|
rID++
|
||||||
var ID bytes.Buffer
|
var ID bytes.Buffer
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOpenFile(t *testing.T) {
|
func TestOpenFile(t *testing.T) {
|
||||||
// Test update a XLSX file.
|
// Test update the spreadsheet file.
|
||||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -154,11 +154,11 @@ func TestOpenFile(t *testing.T) {
|
||||||
// Test read cell value with given axis large than exists row.
|
// Test read cell value with given axis large than exists row.
|
||||||
_, err = f.GetCellValue("Sheet2", "E231")
|
_, err = f.GetCellValue("Sheet2", "E231")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Test get active worksheet of XLSX and get worksheet name of XLSX by given worksheet index.
|
// Test get active worksheet of spreadsheet and get worksheet name of spreadsheet by given worksheet index.
|
||||||
f.GetSheetName(f.GetActiveSheetIndex())
|
f.GetSheetName(f.GetActiveSheetIndex())
|
||||||
// Test get worksheet index of XLSX by given worksheet name.
|
// Test get worksheet index of spreadsheet by given worksheet name.
|
||||||
f.GetSheetIndex("Sheet1")
|
f.GetSheetIndex("Sheet1")
|
||||||
// Test get worksheet name of XLSX by given invalid worksheet index.
|
// Test get worksheet name of spreadsheet by given invalid worksheet index.
|
||||||
f.GetSheetName(4)
|
f.GetSheetName(4)
|
||||||
// Test get worksheet map of workbook.
|
// Test get worksheet map of workbook.
|
||||||
f.GetSheetMap()
|
f.GetSheetMap()
|
||||||
|
@ -261,7 +261,7 @@ func TestBrokenFile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("OpenNotExistsFile", func(t *testing.T) {
|
t.Run("OpenNotExistsFile", func(t *testing.T) {
|
||||||
// Test open a XLSX file with given illegal path.
|
// Test open a spreadsheet file with given illegal path.
|
||||||
_, err := OpenFile(filepath.Join("test", "NotExistsFile.xlsx"))
|
_, err := OpenFile(filepath.Join("test", "NotExistsFile.xlsx"))
|
||||||
if assert.Error(t, err) {
|
if assert.Error(t, err) {
|
||||||
assert.True(t, os.IsNotExist(err), "Expected os.IsNotExists(err) == true")
|
assert.True(t, os.IsNotExist(err), "Expected os.IsNotExists(err) == true")
|
||||||
|
@ -270,7 +270,7 @@ func TestBrokenFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewFile(t *testing.T) {
|
func TestNewFile(t *testing.T) {
|
||||||
// Test create a XLSX file.
|
// Test create a spreadsheet file.
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.NewSheet("Sheet1")
|
f.NewSheet("Sheet1")
|
||||||
f.NewSheet("XLSXSheet2")
|
f.NewSheet("XLSXSheet2")
|
||||||
|
|
10
lib.go
10
lib.go
|
@ -26,10 +26,18 @@ import (
|
||||||
// filesystem.
|
// filesystem.
|
||||||
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
||||||
var err error
|
var err error
|
||||||
|
var docPart = map[string]string{
|
||||||
|
"[content_types].xml": "[Content_Types].xml",
|
||||||
|
"xl/sharedstrings.xml": "xl/sharedStrings.xml",
|
||||||
|
}
|
||||||
fileList := make(map[string][]byte, len(r.File))
|
fileList := make(map[string][]byte, len(r.File))
|
||||||
worksheets := 0
|
worksheets := 0
|
||||||
for _, v := range r.File {
|
for _, v := range r.File {
|
||||||
if fileList[v.Name], err = readFile(v); err != nil {
|
fileName := v.Name
|
||||||
|
if partName, ok := docPart[strings.ToLower(v.Name)]; ok {
|
||||||
|
fileName = partName
|
||||||
|
}
|
||||||
|
if fileList[fileName], err = readFile(v); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(v.Name, "xl/worksheets/sheet") {
|
if strings.HasPrefix(v.Name, "xl/worksheets/sheet") {
|
||||||
|
|
|
@ -474,7 +474,7 @@ func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
|
||||||
return f.getPicture(row, col, drawingXML, drawingRelationships)
|
return f.getPicture(row, col, drawingXML, drawingRelationships)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePicture provides a function to delete charts in XLSX by given
|
// DeletePicture provides a function to delete charts in spreadsheet by given
|
||||||
// worksheet and cell name. Note that the image file won't be deleted from the
|
// worksheet and cell name. Note that the image file won't be deleted from the
|
||||||
// document currently.
|
// document currently.
|
||||||
func (f *File) DeletePicture(sheet, cell string) (err error) {
|
func (f *File) DeletePicture(sheet, cell string) (err error) {
|
||||||
|
@ -496,7 +496,7 @@ func (f *File) DeletePicture(sheet, cell string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPicture provides a function to get picture base name and raw content
|
// getPicture provides a function to get picture base name and raw content
|
||||||
// embed in XLSX by given coordinates and drawing relationships.
|
// embed in spreadsheet by given coordinates and drawing relationships.
|
||||||
func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {
|
func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {
|
||||||
var (
|
var (
|
||||||
wsDr *xlsxWsDr
|
wsDr *xlsxWsDr
|
||||||
|
|
4
rows.go
4
rows.go
|
@ -282,10 +282,6 @@ func (f *File) sharedStringsReader() *xlsxSST {
|
||||||
if f.SharedStrings == nil {
|
if f.SharedStrings == nil {
|
||||||
var sharedStrings xlsxSST
|
var sharedStrings xlsxSST
|
||||||
ss := f.readXML("xl/sharedStrings.xml")
|
ss := f.readXML("xl/sharedStrings.xml")
|
||||||
if len(ss) == 0 {
|
|
||||||
ss = f.readXML("xl/SharedStrings.xml")
|
|
||||||
delete(f.XLSX, "xl/SharedStrings.xml")
|
|
||||||
}
|
|
||||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
|
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(ss))).
|
||||||
Decode(&sharedStrings); err != nil && err != io.EOF {
|
Decode(&sharedStrings); err != nil && err != io.EOF {
|
||||||
log.Printf("xml decode error: %s", err)
|
log.Printf("xml decode error: %s", err)
|
||||||
|
|
33
sheet.go
33
sheet.go
|
@ -32,8 +32,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSheet provides function to create a new sheet by given worksheet name.
|
// NewSheet provides function to create a new sheet by given worksheet name.
|
||||||
// When creating a new XLSX file, the default sheet will be created. Returns
|
// When creating a new spreadsheet file, the default worksheet will be
|
||||||
// the number of sheets in the workbook (file) after appending the new sheet.
|
// created. Returns the number of sheets in the workbook (file) after
|
||||||
|
// appending the new sheet.
|
||||||
func (f *File) NewSheet(name string) int {
|
func (f *File) NewSheet(name string) int {
|
||||||
// Check if the worksheet already exists
|
// Check if the worksheet already exists
|
||||||
if f.GetSheetIndex(name) != -1 {
|
if f.GetSheetIndex(name) != -1 {
|
||||||
|
@ -152,7 +153,7 @@ func trimCell(column []xlsxC) []xlsxC {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setContentTypes provides a function to read and update property of contents
|
// setContentTypes provides a function to read and update property of contents
|
||||||
// type of XLSX.
|
// type of the spreadsheet.
|
||||||
func (f *File) setContentTypes(partName, contentType string) {
|
func (f *File) setContentTypes(partName, contentType string) {
|
||||||
content := f.contentTypesReader()
|
content := f.contentTypesReader()
|
||||||
content.Overrides = append(content.Overrides, xlsxOverride{
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
||||||
|
@ -174,8 +175,8 @@ func (f *File) setSheet(index int, name string) {
|
||||||
f.Sheet[path] = &xlsx
|
f.Sheet[path] = &xlsx
|
||||||
}
|
}
|
||||||
|
|
||||||
// setWorkbook update workbook property of XLSX. Maximum 31 characters are
|
// setWorkbook update workbook property of the spreadsheet. Maximum 31
|
||||||
// allowed in sheet title.
|
// characters are allowed in sheet title.
|
||||||
func (f *File) setWorkbook(name string, sheetID, rid int) {
|
func (f *File) setWorkbook(name string, sheetID, rid int) {
|
||||||
content := f.workbookReader()
|
content := f.workbookReader()
|
||||||
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
||||||
|
@ -204,9 +205,9 @@ func (f *File) setAppXML() {
|
||||||
f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
|
f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// replaceRelationshipsBytes; Some tools that read XLSX files have very strict
|
// replaceRelationshipsBytes; Some tools that read spreadsheet files have very
|
||||||
// requirements about the structure of the input XML. This function is a
|
// strict requirements about the structure of the input XML. This function is
|
||||||
// horrible hack to fix that after the XML marshalling is completed.
|
// a horrible hack to fix that after the XML marshalling is completed.
|
||||||
func replaceRelationshipsBytes(content []byte) []byte {
|
func replaceRelationshipsBytes(content []byte) []byte {
|
||||||
oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
oldXmlns := stringToBytes(`xmlns:relationships="http://schemas.openxmlformats.org/officeDocument/2006/relationships" relationships`)
|
||||||
newXmlns := stringToBytes("r")
|
newXmlns := stringToBytes("r")
|
||||||
|
@ -263,7 +264,7 @@ func (f *File) SetActiveSheet(index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveSheetIndex provides a function to get active sheet index of the
|
// GetActiveSheetIndex provides a function to get active sheet index of the
|
||||||
// XLSX. If not found the active sheet will be return integer 0.
|
// spreadsheet. If not found the active sheet will be return integer 0.
|
||||||
func (f *File) GetActiveSheetIndex() (index int) {
|
func (f *File) GetActiveSheetIndex() (index int) {
|
||||||
var sheetID = f.getActiveSheetID()
|
var sheetID = f.getActiveSheetID()
|
||||||
wb := f.workbookReader()
|
wb := f.workbookReader()
|
||||||
|
@ -278,7 +279,7 @@ func (f *File) GetActiveSheetIndex() (index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getActiveSheetID provides a function to get active sheet index of the
|
// getActiveSheetID provides a function to get active sheet index of the
|
||||||
// XLSX. If not found the active sheet will be return integer 0.
|
// spreadsheet. If not found the active sheet will be return integer 0.
|
||||||
func (f *File) getActiveSheetID() int {
|
func (f *File) getActiveSheetID() int {
|
||||||
wb := f.workbookReader()
|
wb := f.workbookReader()
|
||||||
if wb != nil {
|
if wb != nil {
|
||||||
|
@ -313,9 +314,9 @@ func (f *File) SetSheetName(oldName, newName string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSheetNameByID provides a function to get worksheet name of XLSX by given
|
// getSheetNameByID provides a function to get worksheet name of the
|
||||||
// worksheet ID. If given sheet ID is invalid, will return an empty
|
// spreadsheet by given worksheet ID. If given sheet ID is invalid, will
|
||||||
// string.
|
// return an empty string.
|
||||||
func (f *File) getSheetNameByID(ID int) string {
|
func (f *File) getSheetNameByID(ID int) string {
|
||||||
wb := f.workbookReader()
|
wb := f.workbookReader()
|
||||||
if wb == nil || ID < 1 {
|
if wb == nil || ID < 1 {
|
||||||
|
@ -341,9 +342,9 @@ func (f *File) GetSheetName(index int) (name string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSheetID provides a function to get worksheet ID of XLSX by given
|
// getSheetID provides a function to get worksheet ID of the spreadsheet by
|
||||||
// sheet name. If given worksheet name is invalid, will return an integer type
|
// given sheet name. If given worksheet name is invalid, will return an
|
||||||
// value -1.
|
// integer type value -1.
|
||||||
func (f *File) getSheetID(name string) int {
|
func (f *File) getSheetID(name string) int {
|
||||||
var ID = -1
|
var ID = -1
|
||||||
for sheetID, sheet := range f.GetSheetMap() {
|
for sheetID, sheet := range f.GetSheetMap() {
|
||||||
|
|
Loading…
Reference in New Issue