Golang - tealeg xlsx Sheet Hidden

Golang - tealeg/xlsx Sheet Hidden

概述

本文描述使用tealeg/xlsx时,如何隐藏一个工作表。

问题描述

这个库的Sheet定义如下:

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
    Name        string
    File        *File
    Rows        []*Row
    Cols        []*Col
    MaxRow      int
    MaxCol      int
    Hidden      bool
    Selected    bool
    SheetViews  []SheetView
    SheetFormat SheetFormat
    AutoFilter  *AutoFilter
}

其中的Hidden字段,从字面上来讲,表示是否隐藏该工作表。但实际验证来看,存储文件时,下面的调用并不起作用:

mySheet.Hidden = true

分析

Hidden字段

通过搜索整个源代码,发现在写Excel文件时,并没有用到Sheet的Hidden属性。因此,应用程序设置该字段并不会起任何作用。

针对xlsx文件进行验证

首先验证excel文件中,修改哪个地方可以隐藏工作表。为此,解压缩xlsx文件:

unzip my_file.xlsx -dabc

找到abc/xl/workbook.xml文件,其中某个工作表的定义如下:

<sheet name="my_sheet" sheetId="2" r:id="rId2" state="visible"></sheet><

这里的state是visible,由此导致工作表没有被隐藏。

为了验证,把这里的visible改为hidden,再把整个abc压缩成另外一个Excel xlsx文件:

cd abc
zip -ry ../my_file2.xlsx .

用Office Excel打开,发现工作表被隐藏了。

接下来分析如何修改源码而支持工作表的隐藏功能。

file.go/MarshallParts()

顺着xl/workbook.xml文件,找到存储xlsx文件的一个主要函数。如下:

// Construct a map of file name to XML content representing the file
// in terms of the structure of an XLSX file.
func (f *File) MarshallParts() (map[string]string, error) {
    ...
    if len(f.Sheets)==0 {
        err:= errors.New("Workbook must contains atleast one worksheet")
        return nil, err
    }
    for _, sheet := range f.Sheets {
        ...
        workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
            Name:    sheet.Name,
            SheetId: sheetId,
            Id:      rId,
            State:   "visible"}
        parts[partName], err = marshal(xSheet)
        if err != nil {
            return parts, err
        }
        sheetIndex++
    }

其中的for循环遍历每个工作表,设置相关属性。注意到xlsxSheet这个数据结构,其中的State正是设置隐藏与否这个属性的地方。其定义:

// xlsxSheet directly maps the sheet element from the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
// currently I have not checked it for completeness - it does as much
// as I need.
type xlsxSheet struct {
    Name    string `xml:"name,attr,omitempty"`
    SheetId string `xml:"sheetId,attr,omitempty"`
    Id      string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
    State   string `xml:"state,attr,omitempty"`
}

这些信息和workbook.xml中是完全一致的。

基于这些分析,即可确定代码的修改方法。

方案一

一共三个地方:

  • file.go/MarshallParts(): State = sheet.State
  • sheet.go/Sheet struct: 增加State成员
  • 应用程序定义sheet State属性值

file.go/MarshallParts()

func (f *File) MarshallParts() (map[string]string, error) {
    ...
    for _, sheet := range f.Sheets {
        ...
        workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
            Name:    sheet.Name,
            SheetId: sheetId,
            Id:      rId,
            State:   sheet.State/*"visible"*/} // Modify here.
        ...
    }

sheet.go/Sheet struct

// Sheet is a high level structure intended to provide user access to
// the contents of a particular sheet within an XLSX file.
type Sheet struct {
    Name        string
    File        *File
    Rows        []*Row
    Cols        []*Col
    MaxRow      int
    MaxCol      int
    Hidden      bool
    State       string // New Added
    Selected    bool
    SheetViews  []SheetView
    SheetFormat SheetFormat
    AutoFilter  *AutoFilter
}

应用程序

//ef.shtFail.Hidden = true // Discard 'Hidden' and set 'State'
ef.shtFail.State = "hidden"

遗留问题

以上是尚可使用的解决办法之一。几个遗留问题:

  • 如果用户没有设置Sheet的(新增的)State属性,那么工作表也仍然是Visible状态。对应xml无state属性:

  • 官方定义的三个state值如下。如果用户指定了其他非法值,那么结果未定义。

    // see xlsx/xmlWorkbook.go
    const (
    // sheet state values as defined by
    // http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx
    sheetStateVisible = “visible”
    sheetStateHidden = “hidden”
    sheetStateVeryHidden = “veryHidden”
    )

为此,考虑另外一种修改方法。

方案二

仍然使用Sheet的Hidden属性。但在序列化的地方,增加一个if处理:

func (f *File) MarshallParts() (map[string]string, error) {
    ...
    for _, sheet := range f.Sheets {
        ...
        workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
            Name:    sheet.Name,
            SheetId: sheetId,
            Id:      rId,
            State:   "visible"}
        if sheet.Hidden {
            workbook.Sheets.Sheet[sheetIndex-1].State = sheetStateHidden
        }
        parts[partName], err = marshal(xSheet)
        ...
    }

Ref

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值