support case-sensitive doc parts to improve compatibility

This commit is contained in:
xuri 2020-07-09 01:24:11 +08:00
parent f7bd0729c6
commit 49257c5918
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
7 changed files with 50 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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") {

View File

@ -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

View File

@ -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)

View File

@ -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() {