2016-08-30 11:51:31 +08:00
|
|
|
package excelize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"bytes"
|
2016-09-07 20:09:02 +08:00
|
|
|
"encoding/xml"
|
2016-08-30 11:51:31 +08:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
2016-09-02 11:54:52 +08:00
|
|
|
// ReadZip takes a pointer to a zip.ReadCloser and returns a
|
|
|
|
// xlsx.File struct populated with its contents. In most cases
|
2016-08-30 11:51:31 +08:00
|
|
|
// ReadZip is not used directly, but is called internally by OpenFile.
|
2016-09-06 21:20:24 +08:00
|
|
|
func ReadZip(f *zip.ReadCloser) (map[string]string, int, error) {
|
2016-08-30 11:51:31 +08:00
|
|
|
defer f.Close()
|
|
|
|
return ReadZipReader(&f.Reader)
|
|
|
|
}
|
|
|
|
|
2016-09-02 11:54:52 +08:00
|
|
|
// ReadZipReader can be used to read an XLSX in memory without
|
2016-08-30 11:51:31 +08:00
|
|
|
// touching the filesystem.
|
2016-09-06 21:20:24 +08:00
|
|
|
func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
|
2016-09-05 10:44:32 +08:00
|
|
|
fileList := make(map[string]string)
|
2016-09-06 21:20:24 +08:00
|
|
|
worksheets := 0
|
2016-08-30 11:51:31 +08:00
|
|
|
for _, v := range r.File {
|
2016-09-05 10:44:32 +08:00
|
|
|
fileList[v.Name] = readFile(v)
|
2016-09-06 21:20:24 +08:00
|
|
|
if len(v.Name) > 18 {
|
2017-01-18 14:47:23 +08:00
|
|
|
if v.Name[0:19] == "xl/worksheets/sheet" {
|
2016-09-07 20:09:02 +08:00
|
|
|
var xlsx xlsxWorksheet
|
2016-12-26 23:55:59 +08:00
|
|
|
xml.Unmarshal([]byte(fileList[v.Name]), &xlsx)
|
2016-09-07 20:09:02 +08:00
|
|
|
xlsx = checkRow(xlsx)
|
|
|
|
output, _ := xml.Marshal(xlsx)
|
2016-12-31 23:47:30 +08:00
|
|
|
fileList[v.Name] = replaceWorkSheetsRelationshipsNameSpace(string(output))
|
2016-09-06 21:20:24 +08:00
|
|
|
worksheets++
|
|
|
|
}
|
|
|
|
}
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2016-09-06 21:20:24 +08:00
|
|
|
return fileList, worksheets, nil
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2016-12-26 23:55:59 +08:00
|
|
|
// Read XML content as string.
|
2016-09-05 16:37:15 +08:00
|
|
|
func (f *File) readXML(name string) string {
|
|
|
|
if content, ok := f.XLSX[name]; ok {
|
2016-12-26 23:55:59 +08:00
|
|
|
return content
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
return ``
|
|
|
|
}
|
|
|
|
|
2016-10-19 20:39:44 +08:00
|
|
|
// Update given file content in file list of XLSX.
|
2016-09-05 16:37:15 +08:00
|
|
|
func (f *File) saveFileList(name string, content string) {
|
|
|
|
f.XLSX[name] = XMLHeader + content
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2016-10-19 20:39:44 +08:00
|
|
|
// Read file content as string in a archive file.
|
2016-08-30 11:51:31 +08:00
|
|
|
func readFile(file *zip.File) string {
|
|
|
|
rc, err := file.Open()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
buff := bytes.NewBuffer(nil)
|
|
|
|
io.Copy(buff, rc)
|
|
|
|
rc.Close()
|
|
|
|
return string(buff.Bytes())
|
|
|
|
}
|
|
|
|
|
2016-10-19 20:39:44 +08:00
|
|
|
// Convert integer to Excel sheet column title.
|
2016-08-30 11:51:31 +08:00
|
|
|
func toAlphaString(value int) string {
|
|
|
|
if value < 0 {
|
|
|
|
return ``
|
|
|
|
}
|
|
|
|
var ans string
|
|
|
|
i := value
|
|
|
|
for i > 0 {
|
|
|
|
ans = string((i-1)%26+65) + ans
|
|
|
|
i = (i - 1) / 26
|
|
|
|
}
|
|
|
|
return ans
|
|
|
|
}
|
|
|
|
|
2016-10-19 20:39:44 +08:00
|
|
|
// Convert Excel sheet column title to int.
|
2016-08-30 11:51:31 +08:00
|
|
|
func titleToNumber(s string) int {
|
|
|
|
weight := 0.0
|
|
|
|
sum := 0
|
|
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|
|
|
sum = sum + (int(s[i])-int('A')+1)*int(math.Pow(26, weight))
|
|
|
|
weight++
|
|
|
|
}
|
|
|
|
return sum - 1
|
|
|
|
}
|
|
|
|
|
2016-09-12 17:37:06 +08:00
|
|
|
// letterOnlyMapF is used in conjunction with strings.Map to return
|
2016-10-19 20:39:44 +08:00
|
|
|
// only the characters A-Z and a-z in a string.
|
2016-09-12 17:37:06 +08:00
|
|
|
func letterOnlyMapF(rune rune) rune {
|
|
|
|
switch {
|
|
|
|
case 'A' <= rune && rune <= 'Z':
|
|
|
|
return rune
|
|
|
|
case 'a' <= rune && rune <= 'z':
|
|
|
|
return rune - 32
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2016-09-12 17:37:06 +08:00
|
|
|
return -1
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
|
|
|
|
2016-09-12 17:37:06 +08:00
|
|
|
// intOnlyMapF is used in conjunction with strings.Map to return only
|
|
|
|
// the numeric portions of a string.
|
|
|
|
func intOnlyMapF(rune rune) rune {
|
|
|
|
if rune >= 48 && rune < 58 {
|
|
|
|
return rune
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|
2016-09-12 17:37:06 +08:00
|
|
|
return -1
|
2016-08-30 11:51:31 +08:00
|
|
|
}
|