Convertitore: una riga nella sua forma più semplice, qualsiasi ambiente nella sua massima flessibilità

Il pacchetto converter di Aspose.Cells for Go via C++ Toolkits riduce ogni esigenza di “convertire un foglio di calcolo in un altro formato” a una singola funzione di ingresso:

converter.Convert(source, saveOption, sink) error

Ha solo tre parametri e ciascuno risponde esattamente a una domanda:

ParametroTipoDomanda a cui rispondeValori tipici
sourcedatasource.DataSourceDa dove proviene l’input?FilePathSource(path), BytesSource(data), NewReaderSource(r)
saveOptionsaveoptions.SaveOptionQuale formato di output, come è configurato?formats.Get("pdf") per impostazioni predefinite per nome, oppure pdf.New(pdf.WithOnePagePerSheet(true)) per controllo fine
sinkdatasource.DataSinkDove va l’output?FilePathSink(path), &BytesSink{}, NewWriterSink(w)

Per lo stile più conciso, usa le impostazioni predefinite su tutti e tre gli assi; per lo stile più flessibile, regola ogni manopola indipendentemente. Poiché i tre assi sono indipendenti, lo stesso Convert si adatta naturalmente a file, memoria, HTTP e ambienti batch. (Esiste anche un one‑liner legacy, ConvertSpreadsheetToFile, per il caso semplice file→file — confrontato nella sezione 1 più sotto.) Il resto di questo articolo passa dal più conciso al più flessibile, ampliando lo snippet precedente a ogni passo invece di passare a un’API diversa.


1. Il più semplice: una chiamata per un formato

1.1 Una one-liner integrata file→file: ConvertSpreadsheetToFile

Il pacchetto fornisce anche una voce più vecchia e ancora più breve per il classico caso “converti questo file in quello”. Tutto ciò che serve sono due percorsi:

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)
	}
}

Deprecato. ConvertSpreadsheetToFile è un involucro leggero mantenuto per compatibilità retroattiva; il nuovo codice è indirizzato a converter.Convert qui sotto. Per il caso file→file i due sono esattamente equivalenti — questo è tutto ciò che fa ConvertSpreadsheetToFile.

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 la forma Convert sopra producono risultati identici; ciò che differisce è lo scenario, come mostrato di seguito.

1.2 Il one-liner consigliato: Convert con impostazioni predefinite

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)
	}
}

Tutti e tre i parametri assumono il valore predefinito: input = un file, format = pdf.New() con zero configurazione, output = un file. Questo è il formato “più conciso” — nessun os.Open/os.Create manuale, nessuna allocazione di oggetti, nessuna chiusura di handle; la sorgente/destinazione gestiscono le risorse per te.

1.3 Le due forme file→file confrontate

ConvertSpreadsheetToFile(in, out)converter.Convert(source, opt, sink)
Inputsolo un percorso filequalsiasi DataSource: percorso, []byte, o lettore
Outputsolo un percorso filequalsiasi DataSink: percorso, byte, o io.Writer
Formato di outputsempre inferito dall’estensione, solo valori predefinitiqualsiasi saveOption: predefinito o ottimizzazione specifica del formato
Importazione aggiuntivarichiede la registrazione del formato (_ "…/register", o il sottopacchetto corrispondente saveoptions/*)nessuno, quando si passa un costruttore come pdf.New()
Statowrapper sottile deprecato (stabile, mantenuto per compatibilità)il singolo punto di ingresso documentato per il nuovo codice

Stesso file→esito del file, due scenari diversi:

  • ConvertSpreadsheetToFile — hai letteralmente due percorsi e sei soddisfatto delle impostazioni predefinite del motore per il formato di destinazione. Ideale per script di conversione una tantum, batch di migrazione e helper di test. Il suo prezzo: l’input, l’output e le impostazioni non possono mai uscire dalla forma “file path + defaults” — ed è deprecato.
  • converter.Convert — vuoi lo stesso one-liner, ma uno dei tre argomenti può essere sostituibile: domani l’input è un []byte caricato, la destinazione richiede pdf.WithOnePagePerSheet(true), o l’output deve andare direttamente in una risposta HTTP. Questi sono esattamente gli scenari delle sezioni 3 e 4 — scrivilo una volta con Convert, e ogni modifica è una modifica a un singolo argomento.

2. Il più semplice × N: una funzione per tutti i 32 formati

Il livello successivo di concisione consiste nell’utilizzare la stessa funzione indipendentemente dal formato di destinazione. Poiché saveOption può essere calcolato dall’estensione del file di output, estrarre tale inferenza produce un “convertitore universale di file”:

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")
}

L’uso rimane identico — cambia solo l’estensione dell’output:

$ 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

Che cos’è quell’importazione vuota _ "…/register"?

Ci sono due modi per ottenere un saveOption; questo è l’unico concetto che lo stile “most concise” deve comprendere:

  • Cercalo nel registro per nome del formato (formats.Get): l’estensione deve essere presente nel registro formats. Ogni sottopacchetto saveoptions/* si registra nel proprio init(), ma solo quando importato. Importa i sottopacchetti di cui hai bisogno uno per volta (ad esempio solo pdf, se converti in PDF), oppure importa vuoto …/register una volta, che registra tutti i 32 formati in una volta — il “convertitore universale” sopra si basa su quella singola riga.
  • Chiama direttamente un costruttore (pdf.New(), markdown.New()): aggira il registro, non è necessario importare register; il costo è una riga di import per ogni formato che utilizzi.

In breve: usa _ "…/register" solo quando risolvi tramite formats.Get + l’estensione di output; passare direttamente un costruttore non richiede nulla di extra. L’importazione vuota è innocua ed è sempre presente negli esempi ufficiali.

3. Il più flessibile: ruota ogni manopola indipendentemente

Quando la necessità supera “file to file with default settings”, non si modifica l’API — i tre parametri Convert sono intercambiabili in modo indipendente.

1. Input (origine): da un file, in memoria, a qualsiasi stream

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: il motore sottostante richiede l’accesso casuale all’intero contenuto, quindi ReaderSource memorizza l’intero flusso in buffer al primo accesso; ogni successivo Open fornisce una copia di quel buffer. Usalo con fiducia per HTTP, ma sappi che non è una vera “conversione in streaming”.

2. Formato (saveOption): predefiniti, o ottimizzati

ScenarioStile di scritturaPerché
Alcuni formato con impostazioni predefiniteformats.Get("pdf")ricercato per estensione; non sensibile al maiuscolo/minuscolo; nil se non registrato
Un formato con opzioni specifiche del formatopdf.New(pdf.WithOnePagePerSheet(true))opzioni funzionali
Impostazioni predefinite, formato esplicitoooxml.New() / csv.New() / …un costruttore senza argomenti per formato
Un formato privato personalizzatoformats.Register("fmt", factory)collegare al registro — vedi docs/register.md

Le opzioni funzionali compaiono solo nei formati che le supportano: una pagina per foglio di lavoro in PDF è WithOnePagePerSheet; le immagini in linea in HTML sono WithExportImagesAsBase64; TSV è formats.Get("tsv"). Le opzioni hanno semantica puntatore: omettere = il valore predefinito del motore; passare esplicitamente false conta comunque.

3. Output (sink): un file, byte 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 e WriterSink sono le due chiavi per la “massima flessibilità”: consentono al risultato della conversione di saltare completamente il disco e di fluire direttamente in una risposta HTTP, storage di oggetti, un file zip, ecc. Tutti e tre sono implementazioni diverse dello stesso parametro sink, quindi qualsiasi cosa accetti []byte o un io.Writer può essere collegata al convertitore.

4. Esempi in ambiente reale: un Convert, molte forme

A. Converti in memoria; l’endpoint di download trasmette PDF

Lo scenario comune lato server — un utente carica un file xlsx, il endpoint restituisce un PDF, senza file temporanei:

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())
}

Per distribuire CSV / Excel / HTML invece, sostituisci solo il saveOption (ad esempio formats.Get("csv")); nient’altro cambia.

B. Leggi una volta, esporta in batch in molti formati

Leggi l’input una sola volta, quindi riutilizzalo tramite BytesSource per un numero qualsiasi di output — ideale per “esporta ogni rapporto”.

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. “Salva con nome” nella famiglia di fogli di calcolo (migrazione / compatibilità)

Migrazione di formato all’interno della famiglia dei fogli di calcolo, ad es. xlsx → xls, xlsx → xlsb, xlsx → ods, e i modelli xltx/xltm — ancora un unico Convert, solo il saveOption cambia:

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. Regola le opzioni per formato, solo quando necessario

Le opzioni funzionali compaiono quando hai bisogno di un controllo fine — nota che ogni chiamata qui sotto è reale e correttamente tipizzata:

// 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" / …

Suggerimento: per output a file singolo come CSV/HTML/immagine, il motore esporta solo il foglio di lavoro che considera “attivo” per impostazione predefinita. Per controllare l’ambito, usa le opzioni specifiche del formato — ad es. html.WithExportActiveWorksheetOnly(true) (solo il foglio attivo?), html.WithShowAllSheets(true), o csv.WithExportAllSheets(true). La qualità dell’immagine a livello di pixel, la scelta esatta dei fogli da renderizzare e altri controlli avanzati esistono ancora a livello del motore, ma l’attuale pacchetto image espone solo l’opzione WithImageType — ricorre al binding sottostante se ti serve di più; il toolkit continuerà a incorporare opzioni nel tempo.

E. Conversione batch su una directory

L’operazione quotidiana “converti questa cartella di Excel in PDF” diventa molto breve con l’inferenza dell’estensione:

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. Classificazione degli errori

Gli errori del convertitore sono errori sentinel classificabili; abbinali a errors.Is per una gestione precisa:

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() restituisce ogni estensione attualmente registrata (ordinata, thread‑safe) — utile per generare un elenco di “formati supportati” o un suggerimento di errore.


5. Formati supportati a colpo d’occhio (32 registrati per impostazione predefinita)

FamigliaEstensioni di outputPacchetto di ottimizzazione
Excelxlsx xlsm xltx xltm xls xlsbsaveoptions/ooxml, xls, xlsb
OpenDocumentods xml dif dbfsaveoptions/ods, xml, dif, dbf
Testo / daticsv txt tsv json sqlsaveoptions/csv, txt, json, sqlScript
Documenti / layoutpdf docx pptx xps pcl epubsaveoptions/pdf, docx, pptx, xps, pcl, ebook
Webhtml mdsaveoptions/html, markdown
Immaginipng jpg jpeg gif bmp svg emf tif tiffsaveoptions/image

Per mantenere il binario snello, importa solo i sottopacchetti che utilizzi (ad esempio pdf); per comodità o per un dispatch generico, importa in bianco …/register. L’importazione di un sottopacchetto attiva la sua registrazione; le ricerche nel registro non distinguono tra maiuscole e minuscole; un’estensione non registrata restituisce nil (non un errore), lasciando la decisione di fallback al chiamante.


6. Best practice e avvertenze

  1. Un unico punto di ingresso — non reinventarlo. File-to-file utilizza Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); cambia i sink solo quando ti servono byte o uno stream. Non scrivere manualmente os.Open → ReadAll → … → os.WriteFile; la sorgente/sink gestiscono le risorse e creano le directory genitore per te.
  2. Concorrenza e licenze. Il motore sottostante è nativo: non condividere un oggetto SaveOption/workbook tra goroutine nello stesso processo; per lavori in blocco elabora i file in sequenza o parallelizza tra processi. La build di valutazione limita i caricamenti (circa cento caricamenti NewWorkbook_Stream per processo) — per benchmark di grandi dimensioni, riavvia il processo per azzerare il contatore.
  3. Leggi l’input una sola volta, poi riutilizzalo. Per generare diversi formati da un unico input, prima usa os.ReadFile per caricarlo in un []byte e converti ripetutamente con BytesSource, evitando letture ripetute dal disco e ricaricamenti.
  4. Gli stream vengono bufferizzati. NewReaderSource effettua il buffering al primo accesso, quindi è adatto a input una tantum come il corpo di una richiesta HTTP, non a streaming vero di file molto grandi (usa FilePathSource che punta direttamente al disco per questi casi).
  5. Sappi chi decide il layout dell’output. Convert è “un input → un file di output”. Se ti serve un file per foglio di lavoro o un zip, utilizza manipulator.Split con un FolderSink/ZipSink — non forzarlo tramite Convert.