xlsx

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 23 Imported by: 0

README

xlsx

CI Go Reference Go Report Card

xlsx is a dependency-free Go library for creating and reading Excel .xlsx workbooks incrementally. Writing buffers a batch of rows and streams them to temporary files; reading streams worksheet rows with xml.Decoder. Neither direction needs to hold a whole workbook in memory.

Features

  • Streaming writer with configurable batching, temp directory, buffer size and ZIP compression.
  • Streaming reader with row-bounded memory and in-memory, disk-backed or automatic shared-string storage.
  • Keyed columns, typed cell helpers, formulas, styles, hyperlinks, merged cells, freeze panes, auto-filters, row heights and column widths.
  • Strings, booleans, integers, unsigned integers, floats, time.Time, []byte, and database/sql/driver.Valuer values (including sql.Null*).
  • Structural validation for packages, relationships, content types, cell references, styles and shared strings.
  • Context cancellation, Excel limits, ZIP-bomb protection and clear sentinel errors.
  • No third-party runtime dependencies.
  • Tested on Linux, macOS, and Windows with Go 1.24, 1.25, and 1.26.

Installation

go get github.com/zignd/xlsx@latest

Requires Go 1.24 or newer.

Writing

package main

import (
	"log"

	"github.com/zignd/xlsx"
)

func main() {
	workbook := xlsx.NewWorkbook("report.xlsx")
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Sales"})

	for i := 0; i < 1_000_000; i++ {
		row, err := worksheet.AddRow()
		if err != nil {
			log.Fatal(err)
		}

		product, err := row.AddCell()
		if err != nil {
			log.Fatal(err)
		}
		product.SetString("Keyboard")

		amount, err := row.AddCell()
		if err != nil {
			log.Fatal(err)
		}
		amount.SetFloat(49.90)

		// Flush this batch to disk. Repeat CommitRows to keep memory flat.
		if err := worksheet.CommitRows(); err != nil {
			log.Fatal(err)
		}
	}

	// Worksheets without pending rows are finalized automatically.
	if err := workbook.Commit(); err != nil {
		log.Fatal(err)
	}
}

CommitRows can be called once per row or once per batch. Batching is usually faster:

const batchSize = 10_000

for i := 0; i < total; i++ {
	row, _ := worksheet.AddRow()
	if err := row.Append("Keyboard", 49.90); err != nil {
		log.Fatal(err)
	}
	if (i+1)%batchSize == 0 {
		if err := worksheet.CommitRows(); err != nil {
			log.Fatal(err)
		}
	}
}
if err := worksheet.CommitRows(); err != nil { // flush the final partial batch
	log.Fatal(err)
}
if err := workbook.Commit(); err != nil {
	log.Fatal(err)
}
Keyed columns
worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Sales"})

err := worksheet.DefineColumns([]*xlsx.WorksheetColumn{
	{Key: "product", Value: "Product", Width: 30},
	{Key: "amount", Value: "Amount"},
	{Key: "sold_at", Value: "Sold at", Width: 22},
})
if err != nil {
	log.Fatal(err)
}

row, err := worksheet.AddRow()
if err != nil {
	log.Fatal(err)
}

product, _ := row.AddCellWithKey("product")
product.SetString("Keyboard")

amount, _ := row.AddCellWithKey("amount")
amount.SetFloat(49.90)

if err := worksheet.CommitRows(); err != nil {
	log.Fatal(err)
}
style, err := workbook.AddStyle(xlsx.Style{
	Bold:         true,
	NumberFormat: "#,##0.00",
	FillColor:    "FFF2CC",
	Horizontal:   "center",
})
if err != nil {
	log.Fatal(err)
}

worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Data"})
if err := worksheet.FreezePanes(1, 0); err != nil { // freeze the header row
	log.Fatal(err)
}
if err := worksheet.SetAutoFilter("A1:D3"); err != nil {
	log.Fatal(err)
}
if err := worksheet.MergeCells("A4:B4"); err != nil {
	log.Fatal(err)
}

row, _ := worksheet.AddRow()
amount, _ := row.WriteFloat(1234.5)
amount.SetStyle(style)
amount.SetHyperlink("https://example.com")

formula, _ := row.AddCell()
formula.SetFormula("=SUM(B1:B2)")

Formulas are written without the leading = and the workbook asks Excel to recalculate on open.

Reading

reader, err := xlsx.Open("report.xlsx")
if err != nil {
	log.Fatal(err)
}
defer reader.Close()

for _, info := range reader.Sheets() {
	fmt.Println(info.Name, info.Hidden)
}

sheet, err := reader.OpenSheet("Sales")
if err != nil {
	log.Fatal(err)
}
defer sheet.Close()

for sheet.Next() {
	row := sheet.Row()
	name, ok := row.CellAt(0)
	if !ok {
		continue
	}
	value, err := name.String()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(row.Index, value)
}
if err := sheet.Err(); err != nil {
	log.Fatal(err)
}

OpenReaderAt(io.ReaderAt, size) supports embedding and object storage. Shared strings use SharedStringsAuto by default: small tables stay in memory and large tables are stored in temporary files and read on demand.

Validation

if err := xlsx.Validate("report.xlsx"); err != nil {
	log.Fatal(err)
}

Validate checks ZIP integrity, unsafe entry names, content types, root and worksheet relationships, required parts, XML well-formedness, sheet names, cell references, row/column ordering, style IDs and shared-string indexes. It streams worksheets and therefore stays memory-flat.

Options and temporary files

workbook, err := xlsx.NewWorkbookWithOptions("report.xlsx", xlsx.Options{
	TempDir:       "/var/tmp/xlsx",
	BufferSize:    128 * 1024,
	Compression:   xlsx.CompressionBestSpeed,
	TempDirMode:   0o700,
	FileMode:      0o600,
	SharedStrings: true,
	Context:       ctx,
})
  • TempDir must exist and be writable. The library creates a private xlsx-* subdirectory inside it and never deletes the parent directory.
  • KeepTempFiles leaves the private directory in place for debugging.
  • NewWriter(io.Writer, Options) writes the final ZIP to an arbitrary writer.
  • Workbook.Close/Cleanup are idempotent and safe to defer.

Supported values

Go type Written as
string, []byte, named string and byte-slice types Inline or shared string
bool, named bool types Excel boolean
Signed and unsigned integers Number
float32, float64 Number
time.Time Excel date serial with a yyyy-mm-dd hh:mm:ss style
database/sql/driver.Valuer The value returned by Value()
nil Blank cell

Unsupported values (structs other than time.Time, maps, NaN, infinities, and so on) return an error from CommitRows, wrapped with ErrUnsupportedType.

Dates are written using their wall-clock components (the location is ignored), which is how Excel stores date-times. Both 1900 and 1904 date systems are read.

Limits and safety

  • 1,048,576 rows and 16,384 columns per worksheet.
  • 32,767 characters per text cell and 8,192 characters per formula.
  • 2,079 characters per hyperlink.
  • Configurable limits for temp space, ZIP entries, uncompressed size, rows, columns and cell length on the reader.
  • Reader rejects path traversal, duplicate ZIP entries, encrypted workbooks and XLSB packages with clear sentinel errors.
  • Workbook, Worksheet, Row and the reader are not safe for concurrent use; use one workbook/reader per goroutine.

Protocol conformance

The writer targets ECMA-376 5th edition / ISO/IEC 29500 Transitional SpreadsheetML, which is what Excel writes by default. The test suite includes:

  • structural validation of every generated part,
  • round-trip reader/writer tests,
  • an openpyxl interoperability job that verifies values, dates, styles, hyperlinks, frozen panes, auto-filters, merged cells and formulas,
  • fuzzing of the reader and column conversion.

The OOXML standard continues to evolve through ISO/IEC and Microsoft implementation notes. When changing OOXML output, compare generated files with Excel, LibreOffice, openpyxl and Excelize and update the compatibility tests.

API overview

func NewWorkbook(filePath string) *Workbook
func NewWorkbookWithOptions(filePath string, opts Options) (*Workbook, error)
func NewWriter(w io.Writer, opts Options) (*Workbook, error)

func (wb *Workbook) AddWorksheet(opts *WorksheetOptions) *Worksheet
func (wb *Workbook) AddStyle(style Style) (int, error)
func (wb *Workbook) Commit() error
func (wb *Workbook) Close() error
func (wb *Workbook) Cleanup() error
func (wb *Workbook) TempDir() string
func (wb *Workbook) Worksheets() []*Worksheet

func (ws *Worksheet) DefineColumns(columns []*WorksheetColumn) error
func (ws *Worksheet) AddRow() (*Row, error)
func (ws *Worksheet) CommitRows() error
func (ws *Worksheet) Commit() error
func (ws *Worksheet) MergeCells(ranges ...string) error
func (ws *Worksheet) FreezePanes(rows, columns int) error
func (ws *Worksheet) SetAutoFilter(ref string) error

func (r *Row) AddCell() (*Cell, error)
func (r *Row) AddCellWithKey(key string) (*Cell, error)
func (r *Row) Append(values ...interface{}) error
func (r *Row) WriteString(value string) (*Cell, error)
func (r *Row) WriteInt(value int64) (*Cell, error)
func (r *Row) WriteFloat(value float64) (*Cell, error)
func (r *Row) WriteBool(value bool) (*Cell, error)
func (r *Row) WriteTime(value time.Time) (*Cell, error)

func Open(path string, opts ...ReadOption) (*Reader, error)
func OpenReaderAt(r io.ReaderAt, size int64, opts ...ReadOption) (*Reader, error)
func (r *Reader) Sheets() []SheetInfo
func (r *Reader) OpenSheet(name string) (*SheetReader, error)
func (r *Reader) Close() error
func (s *SheetReader) Next() bool
func (s *SheetReader) Row() *RowData
func (s *SheetReader) Err() error
func (s *SheetReader) Close() error
func Validate(path string, opts ...ReadOption) error
func ValidateReaderAt(r io.ReaderAt, size int64, opts ...ReadOption) error

Errors intended for callers are exported as sentinels and can be checked with errors.Is, for example ErrPendingRows, ErrReadLimit, ErrCorruptFile, ErrEncrypted, ErrUnsupportedFormat, ErrCellTooLong and ErrInvalidStyle.

See the package documentation for the complete API and runnable examples.

Benchmarks

make bench
# or
go test -run '^$' -bench . -benchmem ./...

The suite covers typed writes, shared strings, styles, row buffering, CommitRows, full workbook commits, reading, validation and XML escaping. Results depend on the machine and storage; CI runs a smoke test for every benchmark.

Performance guide

  • Batch CommitRows calls. Flushing once per 1,000–50,000 rows is usually faster than flushing every row and still keeps memory bounded.
  • Use CompressionBestSpeed when CPU is the bottleneck and CompressionBestCompression when file size matters more.
  • Enable SharedStrings when many cells repeat the same text. It reduces file size but keeps a string table in memory.
  • For reading large files, leave shared strings on SharedStringsAuto; disk mode keeps memory flat at the cost of extra temporary I/O.
  • Keep TempDir on a fast local disk. Network filesystems can dominate commit time.
  • Reuse one workbook/reader per goroutine and parallelize across independent files.

Migration from the writer-only release

The v0.1.x API remains available. New code can opt into:

  • NewWorkbookWithOptions for temp-dir, context and compression control.
  • Row.Append/typed Write* methods and Cell.Set* helpers.
  • Workbook.AddStyle, Cell.SetFormula, Cell.SetHyperlink, Worksheet.MergeCells, Worksheet.FreezePanes and Worksheet.SetAutoFilter.
  • Open, Validate and NewWriter.

Existing NewWorkbook, AddWorksheet, AddCell, AddCellWithKey, CommitRows, Commit and Cleanup calls continue to work.

Development

make test        # go test ./...
make test-race   # race detector
make cover       # coverage profile + function report
make cover-check # coverage gate
make interop     # openpyxl interoperability check
make bench       # benchmarks
make lint        # golangci-lint
make check       # tidy + format + vet + race tests + lint

CI runs the test matrix, lint, module tidiness, coverage gate, benchmark, fuzz, gosec, CodeQL, govulncheck and openpyxl interoperability jobs. Dependabot keeps the Go module and GitHub Actions up to date.

Versioning and releases

The project follows Semantic Versioning. Versions are regular Go module tags, so they are published to pkg.go.dev automatically.

To release from the GitHub UI, run the Tag release workflow and choose patch, minor, or major (optionally with a prerelease suffix), or provide an exact tag such as v1.2.3. The workflow runs the tests, computes or validates the tag, creates an annotated tag, and pushes it. The same workflow calls the reusable Release workflow, which validates the SemVer tag, builds, tests, scans for vulnerabilities, and creates the GitHub release with generated notes.

The same flow can be driven locally:

git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3

License

MIT. See LICENSE.

Documentation

Overview

Package xlsx lets you create and read XLSX files incrementally.

The writer buffers rows only until CommitRows is called and then streams them to temporary files on disk. The reader streams worksheet rows with xml.Decoder and keeps only one row at a time in memory. Both directions are designed for large workbooks.

Writing

workbook := xlsx.NewWorkbook("report.xlsx")
worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Sales"})

row, err := worksheet.AddRow()
if err != nil {
	log.Fatal(err)
}
cell, err := row.AddCell()
if err != nil {
	log.Fatal(err)
}
cell.SetString("Revenue")

if err := worksheet.CommitRows(); err != nil {
	log.Fatal(err)
}
if err := workbook.Commit(); err != nil {
	log.Fatal(err)
}

Reading

reader, err := xlsx.Open("report.xlsx")
if err != nil {
	log.Fatal(err)
}
defer reader.Close()

sheet, err := reader.OpenSheet("Sales")
if err != nil {
	log.Fatal(err)
}
defer sheet.Close()

for sheet.Next() {
	cell, ok := sheet.Row().CellAt(0)
	if !ok {
		continue
	}
	value, err := cell.String()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(value)
}
if err := sheet.Err(); err != nil {
	log.Fatal(err)
}

Options and temporary files

NewWorkbookWithOptions accepts a temp directory, buffer size, compression, file modes, context cancellation, shared strings and safety limits. The library creates a private subdirectory inside the configured temp directory and never deletes the parent directory.

NewWriter writes the final package to an io.Writer, which is useful for HTTP responses and object storage.

Values

Cell.Value accepts strings, booleans, all integer and unsigned integer types, floats, time.Time, []byte and database/sql/driver.Valuer implementations. Use Cell.SetFormula for formulas, Cell.SetHyperlink for links and Cell.SetStyle with an ID returned by Workbook.AddStyle.

Validation

Validate checks the package structure, content types, relationships, worksheet references, styles and shared strings without loading the whole workbook into memory.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrWorkbookCommitted is returned when an operation is attempted on a
	// workbook that has already been committed.
	ErrWorkbookCommitted = errors.New("workbook already committed")

	// ErrWorksheetCommitted is returned when an operation is attempted on a
	// worksheet that has already been committed.
	ErrWorksheetCommitted = errors.New("worksheet already committed")

	// ErrNoWorksheets is returned when a workbook without worksheets is
	// committed.
	ErrNoWorksheets = errors.New("a workbook needs at least one worksheet")

	// ErrNoRows is returned when CommitRows is called without pending rows.
	ErrNoRows = errors.New("there are no rows to commit")

	// ErrPendingRows is returned when a worksheet is committed while it still
	// has rows waiting to be written.
	ErrPendingRows = errors.New("worksheet has pending rows")

	// ErrNoColumns is returned when a keyed cell is added to a worksheet that
	// has no columns defined.
	ErrNoColumns = errors.New("no columns were defined on the worksheet")

	// ErrColumnsDefined is returned when an unkeyed cell is added to a
	// worksheet that uses keyed columns.
	ErrColumnsDefined = errors.New("columns were defined on the worksheet")

	// ErrUnsupportedType is returned when a cell value cannot be represented
	// in an XLSX file.
	ErrUnsupportedType = errors.New("unsupported cell value type")

	// ErrInvalidSheetName is returned when a worksheet name is not a valid
	// Excel sheet name.
	ErrInvalidSheetName = errors.New("invalid worksheet name")

	// ErrDuplicateSheetName is returned when two worksheets share a name.
	ErrDuplicateSheetName = errors.New("duplicate worksheet name")

	// ErrInvalidColumn is returned when a column definition is invalid.
	ErrInvalidColumn = errors.New("invalid worksheet column")

	// ErrInvalidStyle is returned when a style or style ID is invalid.
	ErrInvalidStyle = errors.New("invalid cell style")

	// ErrInvalidFormula is returned when a formula is invalid.
	ErrInvalidFormula = errors.New("invalid cell formula")

	// ErrInvalidHyperlink is returned when a hyperlink is invalid.
	ErrInvalidHyperlink = errors.New("invalid cell hyperlink")

	// ErrInvalidRange is returned when an A1 range is invalid.
	ErrInvalidRange = errors.New("invalid A1 range")

	// ErrInvalidRow is returned when a row property is invalid.
	ErrInvalidRow = errors.New("invalid row")

	// ErrCellTooLong is returned when a text value exceeds the cell limit.
	ErrCellTooLong = errors.New("cell value is too long")

	// ErrTooManySheets is returned when a workbook exceeds its sheet limit.
	ErrTooManySheets = errors.New("too many worksheets")

	// ErrReadLimit is returned when a reader hits a configured safety limit.
	ErrReadLimit = errors.New("reader limit exceeded")

	// ErrCorruptFile is returned when an XLSX package is malformed.
	ErrCorruptFile = errors.New("corrupt XLSX file")

	// ErrEncrypted is returned for password-protected workbooks.
	ErrEncrypted = errors.New("encrypted workbooks are not supported")

	// ErrUnsupportedFormat is returned for formats such as XLSB.
	ErrUnsupportedFormat = errors.New("unsupported spreadsheet format")

	// ErrSheetNotFound is returned when a requested sheet does not exist.
	ErrSheetNotFound = errors.New("worksheet not found")

	// ErrSheetClosed is returned when reading from a closed worksheet.
	ErrSheetClosed = errors.New("worksheet reader is closed")

	// ErrInvalidCellReference is returned for malformed A1 references.
	ErrInvalidCellReference = errors.New("invalid cell reference")

	// ErrWrongCellType is returned when a typed cell accessor is used on the
	// wrong cell type.
	ErrWrongCellType = errors.New("wrong cell type")
)

Sentinel errors returned by the package. They can be inspected with errors.Is.

Functions

func Validate added in v0.2.0

func Validate(filePath string, opts ...ValidateOption) error

Validate checks that a file is a structurally valid XLSX package and that its worksheets can be streamed by Reader. It does not recalculate formulas.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func exampleWorkbook() (string, func()) {
	dir, err := os.MkdirTemp("", "xlsx-reader-example-*")
	if err != nil {
		panic(err)
	}
	path := filepath.Join(dir, "people.xlsx")
	workbook := xlsx.NewWorkbook(path)
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "People"})
	for _, person := range [][]interface{}{
		{"Ada", 36},
		{"Grace", 85},
	} {
		row, err := worksheet.AddRow()
		if err != nil {
			panic(err)
		}
		if err := row.Append(person...); err != nil {
			panic(err)
		}
	}
	if err := worksheet.CommitRows(); err != nil {
		panic(err)
	}
	if err := workbook.Commit(); err != nil {
		panic(err)
	}
	return path, func() { _ = os.RemoveAll(dir) }
}

func main() {
	path, cleanup := exampleWorkbook()
	defer cleanup()

	if err := xlsx.Validate(path); err != nil {
		panic(err)
	}
	fmt.Println("valid workbook")

}
Output:
valid workbook

func ValidateReaderAt added in v0.2.0

func ValidateReaderAt(r io.ReaderAt, size int64, opts ...ValidateOption) error

ValidateReaderAt validates an XLSX package from an io.ReaderAt.

Types

type Cell

type Cell struct {

	// Key is the column key used to create the cell, if any.
	Key string
	// Value is the cell value. It is written when the owning row is committed.
	Value interface{}
	// Formula is an optional formula without the leading "=".
	Formula string
	// Hyperlink is an optional external or internal hyperlink target.
	Hyperlink string
	// StyleID is the cell style ID returned by Workbook.AddStyle. Zero means
	// the default style; dates automatically use the date style.
	StyleID int
	// contains filtered or unexported fields
}

Cell represents a cell in a worksheet. A row has a collection of cells.

func (*Cell) Identifier

func (c *Cell) Identifier() string

Identifier returns the A1-style reference of the cell (for example "B3").

func (*Cell) Index

func (c *Cell) Index() int

Index returns the zero-based column index of the cell.

func (*Cell) Row

func (c *Cell) Row() *Row

Row returns the row that owns the cell.

func (*Cell) SetBool added in v0.2.0

func (c *Cell) SetBool(value bool) *Cell

SetBool sets a boolean value.

func (*Cell) SetFloat added in v0.2.0

func (c *Cell) SetFloat(value float64) *Cell

SetFloat sets a floating-point value.

func (*Cell) SetFormula added in v0.2.0

func (c *Cell) SetFormula(formula string) *Cell

SetFormula sets a formula, removing an optional leading "=".

func (c *Cell) SetHyperlink(target string) *Cell

SetHyperlink sets an external URL or an internal reference such as "#Sheet2!A1".

func (*Cell) SetInt added in v0.2.0

func (c *Cell) SetInt(value int64) *Cell

SetInt sets an integer value.

func (*Cell) SetString added in v0.2.0

func (c *Cell) SetString(value string) *Cell

SetString sets a string value.

func (*Cell) SetStyle added in v0.2.0

func (c *Cell) SetStyle(styleID int) *Cell

SetStyle sets the cell style ID.

func (*Cell) SetTime added in v0.2.0

func (c *Cell) SetTime(value time.Time) *Cell

SetTime sets a date/time value.

type CellData added in v0.2.0

type CellData struct {
	Ref       string
	Column    int
	Row       int
	Type      CellType
	Raw       string
	Number    float64
	BoolValue bool
	TimeValue time.Time
	Formula   string
	StyleID   int
	HasValue  bool
}

CellData is a cell returned by a SheetReader.

func (CellData) Bool added in v0.2.0

func (c CellData) Bool() (bool, error)

Bool returns the cell as a boolean.

func (CellData) Float64 added in v0.2.0

func (c CellData) Float64() (float64, error)

Float64 returns the cell as a float64.

func (CellData) Int64 added in v0.2.0

func (c CellData) Int64() (int64, error)

Int64 returns the cell as an int64.

func (CellData) String added in v0.2.0

func (c CellData) String() (string, error)

String returns the most natural string representation of the cell.

func (CellData) Time added in v0.2.0

func (c CellData) Time() (time.Time, error)

Time returns the cell as a date/time.

type CellOptions

type CellOptions struct {
	// Key is the optional column key.
	Key *string
	// Value is the initial cell value.
	Value interface{}
}

CellOptions has options used when creating a new cell.

type CellType added in v0.2.0

type CellType uint8

CellType describes the type of a cell returned by the reader.

const (
	CellTypeBlank CellType = iota
	CellTypeString
	CellTypeNumber
	CellTypeBool
	CellTypeDate
	CellTypeError
)

type Compression added in v0.2.0

type Compression uint8

Compression controls how the final ZIP package is compressed.

const (
	// CompressionDefault uses the standard DEFLATE compression level.
	CompressionDefault Compression = iota
	// CompressionStore writes ZIP entries without compression.
	CompressionStore
	// CompressionBestSpeed favours speed over size.
	CompressionBestSpeed
	// CompressionBestCompression favours size over speed.
	CompressionBestCompression
)

type Options added in v0.2.0

type Options struct {
	// TempDir is the parent directory for temporary worksheet files. The
	// library creates a private subdirectory inside it.
	TempDir string

	// BufferSize is the size of the per-worksheet write buffer. Zero uses the
	// package default.
	BufferSize int

	// TempDirMode is the permission mode for temporary directories.
	TempDirMode os.FileMode

	// FileMode is the permission mode for the final .xlsx file.
	FileMode os.FileMode

	// Compression selects the ZIP compression strategy.
	Compression Compression

	// KeepTempFiles leaves the temporary workbook directory in place after
	// Commit. It is useful for debugging.
	KeepTempFiles bool

	// Context cancels long-running writes. Nil means context.Background().
	Context context.Context

	// SharedStrings writes repeated strings through xl/sharedStrings.xml.
	SharedStrings bool

	// MaxCellLength limits text values. Zero uses Excel's 32,767-character
	// limit.
	MaxCellLength int

	// MaxRows limits rows per worksheet. Zero uses Excel's limit.
	MaxRows int

	// MaxColumns limits columns per worksheet. Zero uses Excel's limit.
	MaxColumns int

	// MaxSheets limits the number of worksheets. Zero uses 1,024.
	MaxSheets int
}

Options configures a workbook writer.

func DefaultOptions added in v0.2.0

func DefaultOptions() Options

DefaultOptions returns the default writer configuration.

type ReadOption added in v0.2.0

type ReadOption func(*ReadOptions)

ReadOption customizes ReadOptions.

func WithReadContext added in v0.2.0

func WithReadContext(ctx context.Context) ReadOption

WithReadContext sets the reader context.

func WithReadLimits added in v0.2.0

func WithReadLimits(maxUncompressedSize int64, maxEntries int) ReadOption

WithReadLimits overrides the reader safety limits.

func WithReadMaxCellLength added in v0.2.0

func WithReadMaxCellLength(length int) ReadOption

WithReadMaxCellLength limits text values.

func WithReadMaxColumns added in v0.2.0

func WithReadMaxColumns(columns int) ReadOption

WithReadMaxColumns limits columns per worksheet.

func WithReadMaxRows added in v0.2.0

func WithReadMaxRows(rows int) ReadOption

WithReadMaxRows limits rows per worksheet.

func WithReadTempDir added in v0.2.0

func WithReadTempDir(dir string) ReadOption

WithReadTempDir sets the parent directory for disk-backed shared strings.

func WithSharedStringsMode added in v0.2.0

func WithSharedStringsMode(mode SharedStringsMode) ReadOption

WithSharedStringsMode sets the shared-string storage mode.

type ReadOptions added in v0.2.0

type ReadOptions struct {
	// Context cancels long-running reads. Nil means context.Background().
	Context context.Context

	// SharedStringsMode selects the shared-string storage strategy.
	SharedStringsMode SharedStringsMode

	// SharedStringsMemoryLimit is the auto-mode threshold in bytes.
	SharedStringsMemoryLimit int64

	// TempDir is the parent directory for disk-backed shared strings.
	TempDir string

	// MaxUncompressedSize limits the total uncompressed package size.
	MaxUncompressedSize int64

	// MaxEntries limits the number of ZIP entries.
	MaxEntries int

	// MaxRows limits rows per worksheet.
	MaxRows int

	// MaxColumns limits columns per worksheet.
	MaxColumns int

	// MaxCellLength limits text values.
	MaxCellLength int
}

ReadOptions configures a streaming XLSX reader.

func DefaultReadOptions added in v0.2.0

func DefaultReadOptions() ReadOptions

DefaultReadOptions returns the default reader configuration.

type Reader added in v0.2.0

type Reader struct {
	// contains filtered or unexported fields
}

Reader is a streaming XLSX reader.

func Open added in v0.2.0

func Open(path string, opts ...ReadOption) (*Reader, error)

Open opens an XLSX file for streaming reads.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func exampleWorkbook() (string, func()) {
	dir, err := os.MkdirTemp("", "xlsx-reader-example-*")
	if err != nil {
		panic(err)
	}
	path := filepath.Join(dir, "people.xlsx")
	workbook := xlsx.NewWorkbook(path)
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "People"})
	for _, person := range [][]interface{}{
		{"Ada", 36},
		{"Grace", 85},
	} {
		row, err := worksheet.AddRow()
		if err != nil {
			panic(err)
		}
		if err := row.Append(person...); err != nil {
			panic(err)
		}
	}
	if err := worksheet.CommitRows(); err != nil {
		panic(err)
	}
	if err := workbook.Commit(); err != nil {
		panic(err)
	}
	return path, func() { _ = os.RemoveAll(dir) }
}

func main() {
	path, cleanup := exampleWorkbook()
	defer cleanup()

	reader, err := xlsx.Open(path)
	if err != nil {
		panic(err)
	}
	defer reader.Close()

	sheet, err := reader.OpenSheet("People")
	if err != nil {
		panic(err)
	}
	defer sheet.Close()

	for sheet.Next() {
		name, _ := sheet.Row().CellAt(0)
		age, _ := sheet.Row().CellAt(1)
		nameValue, _ := name.String()
		ageValue, _ := age.Int64()
		fmt.Printf("%s is %d\n", nameValue, ageValue)
	}
	if err := sheet.Err(); err != nil {
		panic(err)
	}

}
Output:
Ada is 36
Grace is 85

func OpenReaderAt added in v0.2.0

func OpenReaderAt(r io.ReaderAt, size int64, opts ...ReadOption) (*Reader, error)

OpenReaderAt opens an XLSX package from an io.ReaderAt.

func (*Reader) Close added in v0.2.0

func (r *Reader) Close() error

Close releases the package and any temporary reader files.

func (*Reader) OpenSheet added in v0.2.0

func (r *Reader) OpenSheet(name string) (*SheetReader, error)

OpenSheet opens a worksheet for streaming reads.

func (*Reader) Sheets added in v0.2.0

func (r *Reader) Sheets() []SheetInfo

Sheets returns a copy of the workbook's sheet metadata.

type Row

type Row struct {

	// Height is the optional row height in points. Zero means the default.
	Height float64
	// contains filtered or unexported fields
}

Row represents a row in a worksheet. A worksheet has a collection of rows.

func (*Row) AddCell

func (r *Row) AddCell() (*Cell, error)

AddCell adds a new cell to the row. It can only be used on worksheets where columns were not defined.

func (*Row) AddCellWithKey

func (r *Row) AddCellWithKey(key string) (*Cell, error)

AddCellWithKey is just like AddCell but adds a key to the cell. It should be used when columns were defined on the worksheet.

func (*Row) Append added in v0.2.0

func (r *Row) Append(values ...interface{}) error

Append adds a series of values to the row. On keyed worksheets the values are mapped to columns in definition order.

func (*Row) Index

func (r *Row) Index() int

Index returns the zero-based index of the row.

func (*Row) SetHeight added in v0.2.0

func (r *Row) SetHeight(height float64) error

SetHeight sets the row height in points. Excel accepts 0 (default) through 409.

func (*Row) WriteBool added in v0.2.0

func (r *Row) WriteBool(value bool) (*Cell, error)

WriteBool appends a boolean cell.

func (*Row) WriteFloat added in v0.2.0

func (r *Row) WriteFloat(value float64) (*Cell, error)

WriteFloat appends a floating-point cell.

func (*Row) WriteInt added in v0.2.0

func (r *Row) WriteInt(value int64) (*Cell, error)

WriteInt appends an integer cell.

func (*Row) WriteString added in v0.2.0

func (r *Row) WriteString(value string) (*Cell, error)

WriteString appends a string cell. It can only be used on worksheets without keyed columns; use AddCellWithKey for keyed worksheets.

func (*Row) WriteTime added in v0.2.0

func (r *Row) WriteTime(value time.Time) (*Cell, error)

WriteTime appends a date/time cell.

type RowData added in v0.2.0

type RowData struct {
	Index int
	Cells []CellData
}

RowData is one row returned by a SheetReader.

func (*RowData) Cell added in v0.2.0

func (r *RowData) Cell(ref string) (*CellData, bool)

Cell returns the cell with the given A1 reference.

func (*RowData) CellAt added in v0.2.0

func (r *RowData) CellAt(column int) (*CellData, bool)

CellAt returns the cell at the zero-based column index.

type SharedStringsMode added in v0.2.0

type SharedStringsMode uint8

SharedStringsMode controls how shared strings are read.

const (
	// SharedStringsAuto keeps small tables in memory and spills large tables to
	// disk.
	SharedStringsAuto SharedStringsMode = iota
	// SharedStringsMemory keeps every shared string in memory.
	SharedStringsMemory
	// SharedStringsDisk stores shared strings in temporary files and reads them
	// on demand.
	SharedStringsDisk
)

type SheetInfo added in v0.2.0

type SheetInfo struct {
	Name   string
	ID     int
	RelID  string
	Path   string
	State  string
	Hidden bool
}

SheetInfo describes a worksheet in a workbook.

type SheetReader added in v0.2.0

type SheetReader struct {
	// contains filtered or unexported fields
}

SheetReader streams rows from a worksheet.

func (*SheetReader) Close added in v0.2.0

func (s *SheetReader) Close() error

Close releases the worksheet stream.

func (*SheetReader) Err added in v0.2.0

func (s *SheetReader) Err() error

Err returns the first read error.

func (*SheetReader) Info added in v0.2.0

func (s *SheetReader) Info() SheetInfo

Info returns the worksheet metadata.

func (*SheetReader) Next added in v0.2.0

func (s *SheetReader) Next() bool

Next advances to the next row. It returns false at the end of the sheet or after an error; check Err in that case.

func (*SheetReader) Row added in v0.2.0

func (s *SheetReader) Row() *RowData

Row returns the most recently read row. The returned value is only valid until the next call to Next.

type Style added in v0.2.0

type Style struct {
	Bold         bool
	Italic       bool
	Underline    bool
	FontSize     float64
	FontColor    string
	FillColor    string
	NumberFormat string
	Horizontal   string
	Vertical     string
	WrapText     bool
}

Style is a cell style. The zero value is the default workbook style.

type ValidateOption added in v0.2.0

type ValidateOption = ReadOption

ValidateOption customizes Validate and ValidateReaderAt.

type Workbook

type Workbook struct {
	// FilePath is the destination of the XLSX file written by Commit.
	FilePath string
	// contains filtered or unexported fields
}

Workbook represents a spreadsheet workbook.

func NewWorkbook

func NewWorkbook(filePath string) *Workbook

NewWorkbook creates a new workbook, which is the base for every XLSX file. The file is written when Commit is called.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func main() {
	dir, err := os.MkdirTemp("", "xlsx-example-*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	workbook := xlsx.NewWorkbook(filepath.Join(dir, "report.xlsx"))
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Sales"})

	for i := 0; i < 3; i++ {
		row, err := worksheet.AddRow()
		if err != nil {
			panic(err)
		}

		product, err := row.AddCell()
		if err != nil {
			panic(err)
		}
		product.Value = fmt.Sprintf("Product %d", i+1)

		price, err := row.AddCell()
		if err != nil {
			panic(err)
		}
		price.Value = 9.99 * float64(i+1)

		if err := worksheet.CommitRows(); err != nil {
			panic(err)
		}
	}

	if err := workbook.Commit(); err != nil {
		panic(err)
	}

	fmt.Println("created report.xlsx with 3 rows")
}
Output:
created report.xlsx with 3 rows

func NewWorkbookWithOptions added in v0.2.0

func NewWorkbookWithOptions(filePath string, opts Options) (*Workbook, error)

NewWorkbookWithOptions creates a workbook with explicit writer options.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func main() {
	dir, err := os.MkdirTemp("", "xlsx-options-example-*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	workbook, err := xlsx.NewWorkbookWithOptions(filepath.Join(dir, "styled.xlsx"), xlsx.Options{
		TempDir:       dir,
		SharedStrings: true,
	})
	if err != nil {
		panic(err)
	}

	style, err := workbook.AddStyle(xlsx.Style{Bold: true, NumberFormat: "#,##0.00"})
	if err != nil {
		panic(err)
	}
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Data"})
	if err := worksheet.FreezePanes(1, 0); err != nil {
		panic(err)
	}
	row, err := worksheet.AddRow()
	if err != nil {
		panic(err)
	}
	amount, err := row.WriteFloat(1234.5)
	if err != nil {
		panic(err)
	}
	amount.SetStyle(style)
	amount.SetHyperlink("https://example.com")
	formula, err := row.AddCell()
	if err != nil {
		panic(err)
	}
	formula.SetFormula("=A1*2")
	if err := worksheet.CommitRows(); err != nil {
		panic(err)
	}
	if err := workbook.Commit(); err != nil {
		panic(err)
	}

	fmt.Printf("style ID: %d\n", style)
}
Output:
style ID: 2

func NewWriter added in v0.2.0

func NewWriter(w io.Writer, opts Options) (*Workbook, error)

NewWriter creates a workbook that writes its final package to w instead of a file path. The caller owns w; the workbook does not close it.

func (*Workbook) AddStyle added in v0.2.0

func (wb *Workbook) AddStyle(style Style) (int, error)

AddStyle registers a style and returns its style ID. Style ID 0 is the default style and style ID 1 is reserved for dates.

func (*Workbook) AddWorksheet

func (wb *Workbook) AddWorksheet(opts *WorksheetOptions) *Worksheet

AddWorksheet adds a new worksheet to the workbook.

Invalid or duplicate worksheet names are reported when the worksheet (or the workbook) is committed, because this method intentionally keeps the original error-free API.

func (*Workbook) Cleanup

func (wb *Workbook) Cleanup() error

Cleanup removes the temporary files used to build the workbook. It is safe to call multiple times and is called automatically after a successful Commit.

func (*Workbook) Close added in v0.2.0

func (wb *Workbook) Close() error

Close releases temporary resources. It is an idempotent alias for Cleanup and is intended for defer use.

func (*Workbook) Commit

func (wb *Workbook) Commit() error

Commit commits the workbook, persisting the data to the file path supplied to NewWorkbook. Worksheets with no pending rows are finalized automatically.

func (*Workbook) Context added in v0.2.0

func (wb *Workbook) Context() context.Context

Context returns the writer context.

func (*Workbook) HasPendingWorksheets

func (wb *Workbook) HasPendingWorksheets() bool

HasPendingWorksheets indicates whether the workbook has worksheets that have not been finalized yet.

func (*Workbook) TempDir added in v0.2.0

func (wb *Workbook) TempDir() string

TempDir returns the current private temporary directory, or an empty string before temporary files have been created.

func (*Workbook) Worksheets

func (wb *Workbook) Worksheets() []*Worksheet

Worksheets returns a copy of the workbook's worksheet slice.

type Worksheet

type Worksheet struct {
	// contains filtered or unexported fields
}

Worksheet represents a worksheet in a workbook.

func (*Worksheet) AddRow

func (ws *Worksheet) AddRow() (*Row, error)

AddRow adds a new row to the worksheet. Rows are buffered in memory until CommitRows is called.

func (*Worksheet) Commit

func (ws *Worksheet) Commit() error

Commit commits the worksheet, writing its footer and closing the underlying file. Workbook.Commit finalizes worksheets automatically; calling Commit on an already committed worksheet returns ErrWorksheetCommitted.

func (*Worksheet) CommitRows

func (ws *Worksheet) CommitRows() error

CommitRows writes the rows stored in memory to disk. It can be called repeatedly to stream data without keeping the whole worksheet in memory.

func (*Worksheet) DefineColumns

func (ws *Worksheet) DefineColumns(columns []*WorksheetColumn) error

DefineColumns defines the worksheet columns and writes the header row. It is optional; a worksheet can be used without columns by calling AddCell.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func main() {
	dir, err := os.MkdirTemp("", "xlsx-example-*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	workbook := xlsx.NewWorkbook(filepath.Join(dir, "report.xlsx"))
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Sales"})

	err = worksheet.DefineColumns([]*xlsx.WorksheetColumn{
		{Key: "product", Value: "Product", Width: 30},
		{Key: "amount", Value: "Amount"},
	})
	if err != nil {
		panic(err)
	}

	row, err := worksheet.AddRow()
	if err != nil {
		panic(err)
	}
	product, err := row.AddCellWithKey("product")
	if err != nil {
		panic(err)
	}
	product.Value = "Keyboard"
	amount, err := row.AddCellWithKey("amount")
	if err != nil {
		panic(err)
	}
	amount.Value = 49.9

	if err := worksheet.CommitRows(); err != nil {
		panic(err)
	}
	if err := workbook.Commit(); err != nil {
		panic(err)
	}

	fmt.Println("created a keyed worksheet")
}
Output:
created a keyed worksheet

func (*Worksheet) FileName

func (ws *Worksheet) FileName() string

FileName returns the name of the worksheet part inside the XLSX package.

func (*Worksheet) FreezePanes added in v0.2.0

func (ws *Worksheet) FreezePanes(rows, columns int) error

FreezePanes freezes the given number of rows and columns. It must be called before any rows are committed.

func (*Worksheet) ID

func (ws *Worksheet) ID() int

ID returns the one-based identifier of the worksheet.

func (*Worksheet) MergeCells added in v0.2.0

func (ws *Worksheet) MergeCells(ranges ...string) error

MergeCells merges one or more A1 ranges. Ranges must be valid and must not overlap.

Example
package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/zignd/xlsx"
)

func main() {
	dir, err := os.MkdirTemp("", "xlsx-layout-example-*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	workbook := xlsx.NewWorkbook(filepath.Join(dir, "layout.xlsx"))
	worksheet := workbook.AddWorksheet(&xlsx.WorksheetOptions{Name: "Data"})
	if err := worksheet.FreezePanes(1, 0); err != nil {
		panic(err)
	}
	if err := worksheet.SetAutoFilter("A1:B2"); err != nil {
		panic(err)
	}
	if err := worksheet.MergeCells("A3:B3"); err != nil {
		panic(err)
	}
	row, err := worksheet.AddRow()
	if err != nil {
		panic(err)
	}
	if err := row.Append("Name", "Amount"); err != nil {
		panic(err)
	}
	if err := worksheet.CommitRows(); err != nil {
		panic(err)
	}
	if err := workbook.Commit(); err != nil {
		panic(err)
	}
	fmt.Println("layout written")
}
Output:
layout written

func (*Worksheet) Name

func (ws *Worksheet) Name() string

Name returns the worksheet name.

func (*Worksheet) SetAutoFilter added in v0.2.0

func (ws *Worksheet) SetAutoFilter(ref string) error

SetAutoFilter sets the auto-filter range for the worksheet.

type WorksheetColumn

type WorksheetColumn struct {
	// Key identifies the column when calling AddCellWithKey.
	Key string
	// Value is the header text written in the first row.
	Value string
	// Width is the optional column width in Excel units. A value of zero
	// leaves the column at its default width.
	Width float64
}

WorksheetColumn represents a column in a worksheet.

type WorksheetOptions

type WorksheetOptions struct {
	// Name is the worksheet name shown in Excel.
	Name string
}

WorksheetOptions has options used when creating a new worksheet.

Directories

Path Synopsis
internal
testgen command
Command testgen writes a feature-rich workbook used by the interoperability CI job.
Command testgen writes a feature-rich workbook used by the interoperability CI job.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL