Convertidor: una línea en su forma más simple, cualquier entorno en su máxima flexibilidad
El paquete converter de Aspose.Cells for Go via C++ Toolkits reduce cada necesidad de “convertir una hoja de cálculo a otro formato” a una única función de entrada:
converter.Convert(source, saveOption, sink) error
Solo tiene tres parámetros, y cada uno responde exactamente a una pregunta:
| Parámetro | Tipo | Pregunta que responde | Valores típicos |
|---|---|---|---|
source | datasource.DataSource | ¿De dónde proviene la entrada? | FilePathSource(path), BytesSource(data), NewReaderSource(r) |
saveOption | saveoptions.SaveOption | ¿En qué formato se genera la salida y cómo se ajusta? | formats.Get("pdf") para los valores predeterminados por nombre, o pdf.New(pdf.WithOnePagePerSheet(true)) para un control fino |
sink | datasource.DataSink | ¿A dónde se envía la salida? | FilePathSink(path), &BytesSink{}, NewWriterSink(w) |
Para el estilo más conciso, tome la configuración predeterminada en los tres ejes; para el más flexible, gire cada perilla de forma independiente. Como los tres ejes son independientes, el mismo Convert se adapta de forma natural a entornos de archivo, memoria, HTTP y por lotes. (También existe una línea única heredada, ConvertSpreadsheetToFile, para el caso simple archivo→archivo — comparado en la sección 1 a continuación.) El resto de este artículo pasa de lo más conciso a lo más flexible, ampliando el fragmento anterior en cada paso en lugar de cambiar a una API diferente.
1. Lo más sencillo: una llamada para un formato
1.1 Una única línea incorporada de archivo→archivo: ConvertSpreadsheetToFile
El paquete también incluye una entrada más antigua y aún más corta para el caso clásico de “convertir este archivo en ese archivo”. Todo lo que se necesita son dos rutas:
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.
ConvertSpreadsheetToFilees un contenedor ligero mantenido por compatibilidad hacia atrás; el nuevo código se dirige aconverter.Converta continuación. Para el caso archivo→archivo los dos son exactamente equivalentes — esto es todo lo que haceConvertSpreadsheetToFile.
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 y la forma Convert anterior producen resultados idénticos; lo que difiere es el escenario, como se compara a continuación.
1.2 El one-liner recomendado: Convert con valores predeterminados
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)
}
}
Los tres controles toman sus valores predeterminados: entrada = un archivo, formato = pdf.New() sin configuración, salida = un archivo. Esa es la forma “más concisa” — sin os.Open/os.Create manuales, sin asignación de objetos, sin cierre de manejadores; la fuente/absorción gestionan los recursos por ti.
1.3 Las dos formas archivo→archivo comparadas
ConvertSpreadsheetToFile(in, out) | converter.Convert(source, opt, sink) | |
|---|---|---|
| Input | solo una ruta de archivo | cualquier DataSource: ruta, []byte, o lector |
| Output | solo una ruta de archivo | cualquier DataSink: ruta, bytes o io.Writer |
| Output format | siempre inferido a partir de la extensión, solo valores predeterminados | cualquier saveOption: predeterminado o ajuste específico del formato |
| Extra import | requiere que el formato esté registrado (_ "…/register", o el subpaquete correspondiente saveoptions/*) | ninguno, cuando pasas un constructor como pdf.New() |
| Status | envoltorio delgado obsoleto (estable, mantenido por compatibilidad) | el único punto de entrada documentado para código nuevo |
Mismo archivo→resultado del archivo, dos escenarios diferentes:
ConvertSpreadsheetToFile— literalmente tienes dos rutas y estás satisfecho con la configuración predeterminada del motor para el formato de destino. Ideal para scripts de conversión puntuales, lotes de migración y utilidades de prueba. Su precio: la entrada, la salida y la configuración nunca pueden salir de la forma “file path + defaults” — y está obsoleto.converter.Convert— deseas la misma línea única, pero cualquiera de los tres argumentos puede ser reemplazable: mañana la entrada será un[]bytede una carga, el objetivo necesitapdf.WithOnePagePerSheet(true), o la salida debería ir directamente a una respuesta HTTP. Esos son exactamente los escenarios de las secciones 3 y 4 — escríbelo una vez conConvert, y cada cambio es una edición de un solo argumento.
2. Lo más simple × N: una función para los 32 formatos
El siguiente nivel de concisión es usar la misma función sin importar a qué formato apunte. Porque saveOption puede calcularse a partir de la extensión del archivo de salida, extraer esa inferencia produce un “convertidor de archivos universal”:
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")
}
El uso sigue siendo idéntico — solo cambia la extensión de salida:
$ 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
¿Qué es esa importación vacía _ "…/register"?
Hay dos formas de obtener un saveOption; este es el único concepto que el estilo “más conciso” necesita comprender:
- Buscarlo en el registro por nombre de formato (
formats.Get): la extensión debe estar presente en el registroformats. Cada subpaquetesaveoptions/*se registra a sí mismo en su propioinit(), pero solo cuando se importa. Importa los subpaquetes que necesites uno por uno (solopdf, por ejemplo, si conviertes a PDF), o blank-import…/registeronce, lo que registra los 32 formatos de una sola vez — el “convertidor universal” anterior depende de esa única línea. - Llamar a un constructor directamente (
pdf.New(),markdown.New()): omite el registro, no se necesita importarregister; el costo es una línea de importación por cada formato que uses.
En resumen: solo use _ "…/register" cuando resuelva a través de formats.Get + la extensión de salida; pasar un constructor directamente no necesita nada extra. La importación en blanco es inofensiva y siempre está presente en los ejemplos oficiales.
3. Lo más flexible: gira cada perilla de forma independiente
Cuando la necesidad va más allá de “file to file with default settings”, no cambias la API — los tres parámetros de Convert son intercambiables de forma independiente.
1. Entrada (origen): desde un archivo, a la memoria, a cualquier flujo
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: el motor subyacente necesita acceso aleatorio a todo el contenido, por lo que
ReaderSourcealmacena en búfer la secuencia completamente en el primer acceso; cadaOpenposterior sirve una copia de ese búfer. Úselo con confianza para HTTP, solo tenga en cuenta que no es una verdadera “convertir mientras se transmite”.
2. Formato (saveOption): predeterminados, o ajustado
| Escenario | Estilo de escritura | Por qué |
|---|---|---|
| Algún formato con la configuración predeterminada | formats.Get("pdf") | buscado por extensión; sin distinción entre mayúsculas y minúsculas; nil si no está registrado |
| Un formato con opciones específicas del formato | pdf.New(pdf.WithOnePagePerSheet(true)) | opciones funcionales |
| Configuración predeterminada, formato explícito | ooxml.New() / csv.New() / … | un constructor sin argumentos por formato |
| Un formato privado personalizado | formats.Register("fmt", factory) | conectar al registro — ver docs/register.md |
Las opciones funcionales aparecen solo en los formatos que las tienen: una página por hoja de cálculo en PDF es WithOnePagePerSheet; imágenes incrustadas en HTML es WithExportImagesAsBase64; TSV es formats.Get("tsv"). Las opciones son semántica de puntero: omitir = el valor predeterminado del motor; pasar false explícitamente también cuenta.
3. Salida (sink): un archivo, bytes o un 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 y WriterSink son las dos claves para “más flexible”: permiten que el resultado de la conversión omita el disco por completo y fluya directamente a una respuesta HTTP, almacenamiento de objetos, un zip, etc. Los tres son diferentes implementaciones del mismo parámetro sink, por lo que cualquier cosa que acepte []byte o un io.Writer puede conectarse al conversor.
4. Ejemplos en entorno real: un Convert, muchas formas
A. Convertir en memoria; el punto final de descarga transmite PDF
El escenario típico del lado del servidor — un usuario sube un xlsx, el endpoint devuelve un PDF, sin archivos temporales:
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 en su lugar, cambie solo la saveOption (por ejemplo formats.Get("csv")); nada más cambia.
B. Leer una vez, exportar por lotes a muchos formatos
Lee la entrada una sola vez, luego reutilízala a través de BytesSource para cualquier número de salidas — ideal para “exportar cada informe”:
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. “Guardar como” dentro de la familia de hojas de cálculo (migración / compatibilidad)
Migración de formato dentro de la familia de hojas de cálculo, por ejemplo xlsx → xls, xlsx → xlsb, xlsx → ods, y las plantillas xltx/xltm — sigue siendo una sola Convert, solo cambia la saveOption:
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 opciones por formato, solo cuando sea necesario
Las opciones funcionales aparecen cuando necesitas un control fino — ten en cuenta que cada llamada a continuación es real y está tipada correctamente:
// 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" / …
Consejo: para salidas de un solo archivo como CSV/HTML/imagen, el motor exporta solo la hoja de cálculo que considera “activa” por defecto. Para controlar el alcance, use las propias opciones del formato — por ejemplo
html.WithExportActiveWorksheetOnly(true)(¿solo la hoja activa?),html.WithShowAllSheets(true), ocsv.WithExportAllSheets(true). La calidad de imagen a nivel de píxel, la selección exacta de qué hojas se renderizan y otros controles avanzados siguen existiendo en la capa del motor, pero el paqueteimageactual solo expone el interruptorWithImageType— recurra al enlace subyacente si necesita más; el conjunto de herramientas seguirá incorporando opciones con el tiempo.
E. Conversión por lotes sobre un directorio
La tarea cotidiana de “convertir esta carpeta de Excels a PDF” se vuelve muy corta con la inferencia de extensión:
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. Clasificando errores
Los errores del convertidor son errores centinela clasificables; combínalos con errors.Is para un manejo 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() devuelve cada extensión registrada actualmente (ordenada, segura para concurrencia) — útil para generar una lista de “formatos compatibles” o una pista de error.
5. Formatos compatibles de un vistazo (32 registrados por defecto)
| Familia | Extensiones de salida | Paquete de ajuste |
|---|---|---|
| Excel | xlsx xlsm xltx xltm xls xlsb | saveoptions/ooxml, xls, xlsb |
| OpenDocument | ods xml dif dbf | saveoptions/ods, xml, dif, dbf |
| Texto / datos | csv txt tsv json sql | saveoptions/csv, txt, json, sqlScript |
| Documentos / diseño | pdf docx pptx xps pcl epub | saveoptions/pdf, docx, pptx, xps, pcl, ebook |
| Web | html md | saveoptions/html, markdown |
| Imágenes | png jpg jpeg gif bmp svg emf tif tiff | saveoptions/image |
Para mantener el binario liviano, importe solo los subpaquetes que use (como pdf); para mayor comodidad o despacho genérico, importe en blanco …/register. Importar un subpaquete desencadena su registro; las búsquedas en el registro no distinguen entre mayúsculas y minúsculas; una extensión no registrada devuelve nil (no es un error), dejando la decisión de respaldo al llamador.
6. Mejores prácticas y advertencias
- Un único punto de entrada — no lo reinventes. File-to-file usa
Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); solo cambia los sinks cuando necesites bytes o un stream. No implementes manualmenteos.Open → ReadAll → … → os.WriteFile; el source/sink gestiona los recursos y crea los directorios padre por ti. - Concurrencia y licenciamiento. El motor subyacente es nativo: no compartas un objeto
SaveOption/workbook entre goroutines en el mismo proceso; para trabajos masivos procesa los archivos secuencialmente o paraleliza entre procesos. La compilación de evaluación limita las cargas (aproximadamente cien cargasNewWorkbook_Streampor proceso) — para pruebas de gran escala, reinicia el proceso para restablecer el contador. - Lee la entrada una vez y luego reutilízala. Para generar varios formatos a partir de una sola entrada, primero
os.ReadFileen un[]bytey conviértelo repetidamente conBytesSource, evitando lecturas de disco y recargas repetidas. - Los streams se almacenan en búfer.
NewReaderSourcealmacena en búfer en el primer acceso, por lo que es adecuado para entradas de un solo uso como el cuerpo de una solicitud HTTP, no para streaming real de archivos muy grandes (usaFilePathSourceapuntando directamente al disco para esos casos). - Sabe quién decide el diseño de salida.
Convertes “one input → one output file”. Si necesitas un archivo por hoja de cálculo o un zip, eso esmanipulator.Splitcon unFolderSink/ZipSink— no lo fuerces a pasar porConvert.
