Conversor: uma linha em sua forma mais simples, qualquer ambiente em sua forma mais flexível

O pacote converter do Aspose.Cells for Go via C++ Toolkits reduz cada necessidade de “transformar uma planilha em outro formato” a uma única função de entrada:

converter.Convert(source, saveOption, sink) error

Ele tem apenas três parâmetros, e cada um responde exatamente a uma pergunta:

ParâmetroTipoPergunta que respondeValores típicos
sourcedatasource.DataSourceDe onde vem a entrada?FilePathSource(path), BytesSource(data), NewReaderSource(r)
saveOptionsaveoptions.SaveOptionQual o formato de saída e como está ajustado?formats.Get("pdf") para padrões por nome, ou pdf.New(pdf.WithOnePagePerSheet(true)) para controle fino
sinkdatasource.DataSinkPara onde a saída vai?FilePathSink(path), &BytesSink{}, NewWriterSink(w)

Para o estilo mais conciso, use o padrão em todos os três eixos; para o mais flexível, ajuste cada controle independentemente. Como os três eixos são independentes, o mesmo Convert se adapta naturalmente a ambientes de arquivo, memória, HTTP e lote. (Existe também um one‑liner legado, ConvertSpreadsheetToFile, para o caso simples de arquivo→arquivo — comparado na seção 1 abaixo.) O restante deste artigo passa do mais conciso ao mais flexível, expandindo o trecho anterior a cada passo em vez de mudar para uma API diferente.


1. O mais simples: uma chamada para um formato

1.1 Um one-liner interno de arquivo→arquivo: ConvertSpreadsheetToFile

O pacote também inclui uma entrada mais antiga e ainda mais curta para o caso clássico “converter este arquivo naquele arquivo”. Tudo o que é necessário são dois caminhos:

package main

import (
	"log"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/core"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/converter"
	_ "github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/register" // registers every output format
)

func main() {
	defer core.Cleanup()
	// Both ends are files; the output format is inferred from the extension.
	if err := converter.ConvertSpreadsheetToFile("input.xlsx", "output.pdf"); err != nil {
		log.Fatal(err)
	}
}

Obsoleto. ConvertSpreadsheetToFile é um wrapper fino mantido para compatibilidade retroativa; novo código é direcionado para converter.Convert abaixo. Para o caso arquivo→arquivo, os dois são exatamente equivalentes — isso é tudo que ConvertSpreadsheetToFile faz:

opt := formats.Get(strings.TrimPrefix(filepath.Ext("output.pdf"), ".")) // "pdf"
converter.Convert(
	datasource.FilePathSource("input.xlsx"), // input: a file
	opt,                                     // format: taken from the extension
	datasource.FilePathSink("output.pdf"),   // output: a file
)

ConvertSpreadsheetToFile e a forma Convert acima produzem resultados idênticos; o que difere é o cenário, como comparado a seguir.

1.2 A linha única recomendada: Convert com padrões

package main

import (
	"log"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/core"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/converter"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/datasource"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/saveoptions/pdf"
)

func main() {
	defer core.Cleanup()
	if err := converter.Convert(
		datasource.FilePathSource("input.xlsx"), // input: a file
		pdf.New(),                               // format: PDF, all defaults
		datasource.FilePathSink("output.pdf"),   // output: a file
	); err != nil {
		log.Fatal(err)
	}
}

Todos os três parâmetros assumem seus valores padrão: input = um arquivo, format = pdf.New() sem nenhuma configuração, output = um arquivo. Essa é a forma “mais concisa” — sem os.Open/os.Create manual, sem alocação de objeto, sem fechamento de manipulador; a fonte/destino gerenciam os recursos para você.

1.3 As duas formas file→file comparadas

ConvertSpreadsheetToFile(in, out)converter.Convert(source, opt, sink)
Entradaapenas um caminho de arquivoqualquer DataSource: caminho, []byte, ou leitor
Saídaapenas um caminho de arquivoqualquer DataSink: caminho, bytes, ou io.Writer
Formato de saídasempre inferido a partir da extensão, apenas padrõesqualquer saveOption: padrão ou ajuste específico ao formato
Importação extranecessita que o formato seja registrado (_ "…/register", ou o subpacote correspondente saveoptions/*)nenhum, quando você passa um construtor como pdf.New()
Statuswrapper fino obsoleto (estável, mantido para compatibilidade)o ponto de entrada único documentado para novo código

Mesmo arquivo→resultado do arquivo, dois cenários diferentes:

  • ConvertSpreadsheetToFile — você literalmente tem dois caminhos e está satisfeito com as configurações padrão do mecanismo para o formato de destino. Ideal para scripts de conversão pontuais, lotes de migração e auxiliares de teste. Seu custo: a entrada, a saída e as configurações nunca podem sair da forma “caminho do arquivo + padrões” — e está obsoleto.
  • converter.Convert — você quer a mesma linha única, mas qualquer um dos três argumentos pode ser substituído: amanhã a entrada será um []byte de upload, o destino precisa de pdf.WithOnePagePerSheet(true), ou a saída deve ir direto para uma resposta HTTP. Esses são exatamente os cenários das seções 3 e 4 — escreva uma vez com Convert, e cada mudança é uma edição de um argumento.

2. O mais simples × N: uma função para todos os 32 formatos

O próximo nível de concisão é usar a mesma função independentemente do formato que você deseja. Como saveOption pode ser calculado a partir da extensão do arquivo de saída, extrair essa inferência resulta em um “universal file converter”:

package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/core"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/converter"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/datasource"
	toolkiterrors "github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/errors"
	"github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/formats"
	_ "github.com/aspose-cells/aspose-cells-go-cpp-toolkits/v26/register" // registers every output format
)

// convert re-saves in as out; the output format is read from out's extension.
// The same code handles every built-in format (pdf/csv/html/md/xlsx/png/…).
// It is exactly what the deprecated ConvertSpreadsheetToFile does internally.
func convert(in, out string) error {
	opt := formats.Get(strings.TrimPrefix(filepath.Ext(out), ".")) // ".pdf" -> "pdf"
	if opt == nil {
		return fmt.Errorf("%w: %q", toolkiterrors.ErrUnsupportedFormat, filepath.Ext(out))
	}
	return converter.Convert(
		datasource.FilePathSource(in), // where the input comes from
		opt,                           // what to produce: decided by the extension
		datasource.FilePathSink(out),  // where the output goes
	)
}

func main() {
	defer core.Cleanup()
	if len(os.Args) != 3 {
		log.Fatalf("usage: %s <input.xlsx> <output.ext>", os.Args[0])
	}
	if err := convert(os.Args[1], os.Args[2]); err != nil {
		if errors.Is(err, toolkiterrors.ErrUnsupportedFormat) {
			log.Printf("no registered output format for %q", filepath.Ext(os.Args[2]))
		}
		log.Fatal(err)
	}
	fmt.Println("done")
}

O uso permanece idêntico — apenas a extensão de saída muda:

$ go run . input.xlsx report.pdf     # to PDF
$ go run . input.xlsx report.csv     # to CSV
$ go run . input.xlsx report.html    # to HTML
$ go run . input.xlsx notes.md       # to Markdown
$ go run . input.xlsx sheet.png      # to a PNG image
$ go run . input.xlsx legacy.xls     # to legacy Excel

O que é essa importação vazia _ "…/register"?

Existem duas maneiras de obter um saveOption; este é o único conceito que o estilo “mais conciso” precisa entender:

  • Consulte-o no registro pelo nome do formato (formats.Get): a extensão deve estar presente no registro formats. Cada subpacote saveoptions/* registra a si mesmo em seu próprio init(), mas somente quando importado. Importe os subpacotes que você precisa um a um (apenas pdf, por exemplo, se você converte para PDF), ou importe em branco …/register uma vez, o que registra todos os 32 formatos de uma só vez — o “conversor universal” acima depende dessa única linha.
  • Chame um construtor diretamente (pdf.New(), markdown.New()): contorna o registro, não é necessário importar register; o custo é uma linha de importação por formato que você usar.

Em resumo: use _ "…/register" apenas quando você resolve via formats.Get + a extensão de saída; passar um construtor diretamente não requer nada extra. A importação em branco é inofensiva e está sempre presente nos exemplos oficiais.

3. O mais flexível: ajuste cada botão independentemente

Quando a necessidade vai além de “arquivo para arquivo com configurações padrão”, você não altera a API — os três parâmetros Convert podem ser trocados independentemente.

1. Entrada (origem): de um arquivo, para memória, para qualquer fluxo

datasource.FilePathSource("input.xlsx") // a file on disk
datasource.BytesSource(data)            // []byte already in memory (downloads, cached reuse)
datasource.NewReaderSource(r.Body)      // any io.Reader (HTTP body, compressed stream, …)

Nota: o mecanismo subjacente precisa de acesso aleatório a todo o conteúdo, portanto ReaderSource armazena em buffer o fluxo completamente no primeiro acesso; cada Open posterior serve uma cópia desse buffer. Use‑o com confiança para HTTP, apenas saiba que não é uma verdadeira “conversão enquanto transmite”.

2. Formato (saveOption): padrões, ou ajustado

CenárioEstilo de escritaPor quê
Algum formato com configurações padrãoformats.Get("pdf")pesquisado por extensão; sem distinção entre maiúsculas e minúsculas; nil se não registrado
Um formato com opções específicas de formatopdf.New(pdf.WithOnePagePerSheet(true))opções funcionais
Configurações padrão, formato explícitoooxml.New() / csv.New() / …um construtor sem argumentos por formato
Um formato privado personalizadoformats.Register("fmt", factory)conectar ao registro — veja docs/register.md

Opções funcionais aparecem apenas nos formatos que as possuem: uma página por planilha no PDF é WithOnePagePerSheet; imagens embutidas no HTML são WithExportImagesAsBase64; TSV é formats.Get("tsv"). As opções são semântica de ponteiro: omitir = o padrão do mecanismo; passar false explicitamente também conta.

3. Saída (sink): um arquivo, bytes ou um io.Writer

datasource.FilePathSink("output.pdf") // to disk (parent dirs are created)
&out                                 // into memory; read it back with out.Bytes()
datasource.NewWriterSink(w)          // straight into any io.Writer (http.ResponseWriter, buffer, socket)

BytesSink e WriterSink são as duas chaves para “mais flexível”: eles permitem que o resultado da conversão ignore completamente o disco e flua diretamente para uma resposta HTTP, armazenamento de objetos, um zip, etc. Todos os três são implementações diferentes do mesmo parâmetro sink, portanto qualquer coisa que aceite []byte ou um io.Writer pode ser conectada ao conversor.

4. Exemplos em ambiente real: um Convert, muitas formas

A. Converter na memória; o endpoint de download transmite PDF

O cenário comum do lado do servidor — um usuário envia xlsx, o endpoint retorna PDF, sem arquivos temporários:

func handleExcelToPDF(w http.ResponseWriter, r *http.Request) {
	var out datasource.BytesSink
	if err := converter.Convert(
		datasource.NewReaderSource(r.Body),    // input: the request body
		pdf.New(pdf.WithOnePagePerSheet(true)), // format: PDF, one page per sheet
		&out,                                   // output: memory
	); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	w.Header().Set("Content-Type", "application/pdf")
	w.Header().Set("Content-Disposition", `attachment; filename="report.pdf"`)
	_, _ = w.Write(out.Bytes())
}

Para entregar CSV / Excel / HTML em vez disso, troque apenas o saveOption (por exemplo formats.Get("csv")); nada mais muda.

B. Leia uma vez, exporte em lote para vários formatos

Leia a entrada uma única vez, depois reutilize-a através de BytesSource para qualquer número de saídas — ideal para “exportar cada relatório”:

data, err := os.ReadFile("input.xlsx")
if err != nil {
	log.Fatal(err)
}

for _, ext := range []string{"pdf", "csv", "html", "json", "md"} {
	var out datasource.BytesSink
	if err := converter.Convert(datasource.BytesSource(data), formats.Get(ext), &out); err != nil {
		log.Fatalf("%s: %v", ext, err)
	}
	if err := os.WriteFile("output."+ext, out.Bytes(), 0o644); err != nil {
		log.Fatal(err)
	}
}

C. “Salvar como” dentro da família de planilhas (migração / compatibilidade)

Migração de formato dentro da família de planilhas, por exemplo xlsx → xls, xlsx → xlsb, xlsx → ods, e os modelos xltx/xltm — ainda um Convert, apenas a saveOption muda:

src := datasource.FilePathSource("input.xlsx")
converter.Convert(src, formats.Get("xls"),  datasource.FilePathSink("legacy.xls")) // legacy Excel
converter.Convert(src, formats.Get("xlsb"), datasource.FilePathSink("binary.xlsb")) // binary Excel
converter.Convert(src, formats.Get("ods"),  datasource.FilePathSink("open.ods"))    // OpenDocument
converter.Convert(src, formats.Get("xltx"), datasource.FilePathSink("template.xltx")) // template

D. Ajustar opções por formato, somente quando necessário

As opções funcionais aparecem quando você precisa de controle fino — observe que cada chamada abaixo é real e tipada corretamente:

// PDF: one page per worksheet + a compliance level
pdf.New(
	pdf.WithOnePagePerSheet(true),
	pdf.WithCompliance("pdf15"),
	pdf.WithProducer("my-report-app"),
)

// HTML: single file, images inlined as base64, CSS prefix (avoids style clashes)
html.New(
	html.WithSaveAsSingleFile(true),
	html.WithExportImagesAsBase64(true),
	html.WithCellCssPrefix("tbl_"),
)

// CSV: custom separator and encoding
// (the separator is a byte, and the encoding is a name — matched case- and
// punctuation-insensitively, so "UTF-8", "utf8" and "UTF8" are one name)
csv.New(
	csv.WithSeparator(';'),
	csv.WithEncoding("UTF-8"),
)

// Image: pick the type (the only switch the image package currently exposes)
image.New(image.WithImageType("png")) // or "jpg" / "svg" / "gif" / "tif" / …

Dica: para saídas de arquivo único, como CSV/HTML/imagem, o mecanismo exporta apenas a planilha que considera “ativa” por padrão. Para controlar o escopo, use as próprias opções do formato — por exemplo, html.WithExportActiveWorksheetOnly(true) (apenas a planilha ativa?), html.WithShowAllSheets(true), ou csv.WithExportAllSheets(true). Qualidade de imagem em nível de pixel, escolha exata de quais planilhas são renderizadas e outros controles avançados ainda existem na camada do mecanismo, mas o pacote image atual expõe apenas a opção WithImageType — acesse a ligação subjacente se precisar de mais; o conjunto de ferramentas continuará incorporando opções ao longo do tempo.

E. Conversão em lote em um diretório

O trabalho cotidiano “converter esta pasta de Excels para PDF” torna-se muito curto com inferência de extensão:

func convertAll(inputDir, outputDir string) error {
	if err := os.MkdirAll(outputDir, 0o755); err != nil {
		return err
	}
	return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return err
		}
		ext := strings.ToLower(filepath.Ext(path))
		if ext != ".xlsx" && ext != ".xls" && ext != ".xlsm" && ext != ".csv" {
			return nil
		}
		out := filepath.Join(outputDir, strings.TrimSuffix(filepath.Base(path), ext)+".pdf")
		if err := convert(path, out); err != nil { // reuse the convert from section 2
			return fmt.Errorf("convert %s: %w", path, err)
		}
		fmt.Printf("done: %s -> %s\n", path, out)
		return nil
	})
}

F. Classificando erros

Os erros do conversor são erros sentinela classificáveis; combine‑os com errors.Is para um tratamento preciso:

err := convert("in.xlsx", "out.xyz")
switch {
case errors.Is(err, toolkiterrors.ErrUnsupportedFormat):
	log.Println("this extension has no registered output format")
case errors.Is(err, os.ErrNotExist):
	log.Println("the input file does not exist")
default:
	log.Printf("conversion failed: %v", err)
}

formats.List() retorna todas as extensões atualmente registradas (ordenadas, seguras para concorrência) — útil para gerar uma lista de “formatos suportados” ou uma dica de erro.

5. Formatos suportados em um relance (32 registrados por padrão)

FamíliaExtensões de saídaPacote de ajuste
Excelxlsx xlsm xltx xltm xls xlsbsaveoptions/ooxml, xls, xlsb
OpenDocumentods xml dif dbfsaveoptions/ods, xml, dif, dbf
Texto / dadoscsv txt tsv json sqlsaveoptions/csv, txt, json, sqlScript
Documentos / layoutpdf docx pptx xps pcl epubsaveoptions/pdf, docx, pptx, xps, pcl, ebook
Webhtml mdsaveoptions/html, markdown
Imagenspng jpg jpeg gif bmp svg emf tif tiffsaveoptions/image

Para manter o binário enxuto, importe apenas os subpacotes que você usa (como pdf); para conveniência ou despacho genérico, faça um import em branco de …/register. Importar um subpacote aciona seu registro; as buscas no registro não diferenciam maiúsculas de minúsculas; uma extensão não registrada retorna nil (não é um erro), deixando a decisão de fallback ao chamador.


6. Melhores práticas e ressalvas

  1. Um ponto de entrada — não o reinvente. File-to-file usa Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); troque os sinks somente quando precisar de bytes ou de um stream. Não implemente manualmente os.Open → ReadAll → … → os.WriteFile; o source/sink gerencia recursos e cria diretórios pai para você.

  2. Concorrência e licenciamento. O mecanismo subjacente é nativo: não compartilhe um objeto SaveOption/workbook entre goroutines no mesmo processo; para trabalhos em massa, processe os arquivos sequencialmente ou paralelize entre processos. A compilação de avaliação limita carregamentos (cerca de cem carregamentos NewWorkbook_Stream por processo) — para benchmarks grandes, reinicie o processo para resetar o contador.

  3. Leia a entrada uma vez, depois reutilize-a. Para gerar vários formatos a partir de uma única entrada, use os.ReadFile para carregá‑la em um []byte primeiro e converta repetidamente com BytesSource, evitando leituras repetidas do disco e recarregamentos.

  4. Streams são armazenados em buffer. NewReaderSource faz buffer no primeiro acesso, portanto é adequado para entradas pontuais como o corpo de uma requisição HTTP, não para streaming real de arquivos muito grandes (use FilePathSource apontando diretamente para o disco nesses casos).

  5. Saiba quem decide o layout de saída. Convert é “uma entrada → um arquivo de saída”. Se precisar de um arquivo por planilha ou de um zip, isso é feito com manipulator.Split usando um FolderSink/ZipSink — não force isso através do Convert.