Konverter: eine Zeile in ihrer einfachsten Form, jede Umgebung in ihrer flexibelsten Form

Das converter-Paket von Aspose.Cells for Go via C++ Toolkits reduziert jede Anforderung, ein Tabellenblatt in ein anderes Format zu konvertieren, auf eine Einstiegfunktion:

converter.Convert(source, saveOption, sink) error

Es hat nur drei Parameter, und jeder beantwortet genau eine Frage:

ParameterTypFrage, die sie beantwortetTypische Werte
sourcedatasource.DataSourceWoher kommt die Eingabe?FilePathSource(path), BytesSource(data), NewReaderSource(r)
saveOptionsaveoptions.SaveOptionAls was ausgeben, wie eingestellt?formats.Get("pdf") für Standardwerte per Name, oder pdf.New(pdf.WithOnePagePerSheet(true)) für feine Steuerung
sinkdatasource.DataSinkWohin geht die Ausgabe?FilePathSink(path), &BytesSink{}, NewWriterSink(w)

Für den kompaktesten Stil nehmen Sie die Standardeinstellungen auf allen drei Achsen; für den flexibelsten Stil drehen Sie jeden Regler unabhängig. Da die drei Achsen unabhängig sind, passt das gleiche Convert natürlich in Datei-, Speicher-, HTTP- und Batch-Umgebungen. (Es gibt auch einen Legacy‑Einzeiler, ConvertSpreadsheetToFile, für den einfachen Datei→Datei‑Fall — verglichen in Abschnitt 1 unten.) Der Rest dieses Artikels bewegt sich vom kompaktesten zum flexibelsten Ansatz und erweitert bei jedem Schritt das vorherige Snippet, anstatt zu einer anderen API zu wechseln.


1. Der einfachste: ein Aufruf für ein Format

1.1 Ein eingebauter Datei→Datei-Einzeiler: ConvertSpreadsheetToFile

Das Paket liefert auch einen älteren, noch kürzeren Eintrag für den klassischen “diese Datei in jene Datei konvertieren”-Fall. Alles, was man braucht, sind zwei Pfade:

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

Veraltet. ConvertSpreadsheetToFile ist ein dünner Wrapper, der aus Gründen der Rückwärtskompatibilität beibehalten wird; neuer Code wird unten zu converter.Convert geleitet. Für den Datei→Datei‑Fall sind die beiden exakt gleichwertig — das ist alles, was ConvertSpreadsheetToFile tut:

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 und das oben genannte Convert‑Formular erzeugen identische Ergebnisse; was sich unterscheidet, ist das Szenario, wie im Folgenden verglichen wird.

1.2 Der empfohlene Einzeiler: Convert mit Standardeinstellungen

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

Alle drei Regler übernehmen ihre Standardwerte: Eingabe = eine Datei, Format = pdf.New() ohne Konfiguration, Ausgabe = eine Datei. Das ist die “kompakteste” Form — kein manuelles os.Open/os.Create, keine Objektallokation, kein Schließen von Handles; die source/sink verwalten Ressourcen für Sie.

1.3 Die beiden Datei→Datei-Formen im Vergleich

ConvertSpreadsheetToFile(in, out)converter.Convert(source, opt, sink)
Eingabenur ein Dateipfadbeliebige DataSource: Pfad, []byte, oder Reader
Ausgabenur ein Dateipfadbeliebige DataSink: Pfad, Bytes, oder io.Writer
Ausgabeformatimmer aus der Erweiterung abgeleitet, nur Standardwertebeliebige saveOption: Standard oder formatabhängige Feinabstimmung
Extra-Importerfordert die Registrierung des Formats (_ "…/register", oder das passende saveoptions/* Unterpaket)keine, wenn Sie einen Konstruktor wie pdf.New()
Statusveralteter dünner Wrapper (stabil, aus Kompatibilitätsgründen behalten)der dokumentierte einzelne Einstiegspunkt für neuen Code

Gleiche Datei→Dateiergebnis, zwei verschiedene Szenarien:

  • ConvertSpreadsheetToFile — Sie haben buchstäblich zwei Pfade und sind mit den Standardeinstellungen der Engine für das Zielformat zufrieden. Ideal für einmalige Konvertierungsskripte, Migrationsbatches und Testhilfen. Der Preis: Eingabe, Ausgabe und Einstellungen können nie die Form „Dateipfad + Vorgaben“ verlassen — und es ist veraltet.
  • converter.Convert — Sie möchten denselben Einzeiler, aber eines der drei Argumente soll austauschbar bleiben: morgen ist die Eingabe ein []byte‑Upload, das Ziel benötigt pdf.WithOnePagePerSheet(true), oder die Ausgabe soll direkt in eine HTTP‑Antwort gehen. Das sind genau die Szenarien aus Abschnitt 3 und 4 — schreiben Sie es einmal mit Convert und jede Änderung ist eine ein‑Argument‑Bearbeitung.

2. Das einfachste × N: eine Funktion für alle 32 Formate

Die nächste Stufe der Prägnanz besteht darin, die gleiche Funktion zu verwenden, egal welches Format Sie anvisieren. Da saveOption aus der Dateierweiterung der Ausgabedatei berechnet werden kann, führt das Herausziehen dieser Ableitung zu einem “universellen Dateikonverter”:

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

Die Verwendung bleibt identisch — nur die Ausgabedateierweiterung ändert sich:

$ 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

Was ist dieser leere _ "…/register" Import?

Es gibt zwei Wege, ein saveOption zu erhalten; dies ist das einzige Konzept, das der “most concise”-Stil verstehen muss:

  • Im Register nach dem Formatnamen suchen (formats.Get): Die Erweiterung muss im formats‑Register vorhanden sein. Jeder saveoptions/*‑Unterpaket registriert sich selbst in seiner eigenen init(), jedoch nur beim Import. Importieren Sie die Unterpakete, die Sie benötigen, einzeln (z. B. nur pdf, wenn Sie nach PDF konvertieren), oder blank‑importieren Sie …/register einmal, wodurch alle 32 Formate auf einmal registriert werden – der oben erwähnte „universelle Konverter“ beruht auf dieser einzigen Zeile.
  • Einen Konstruktor direkt aufrufen (pdf.New(), markdown.New()): Umgeht das Register, kein register‑Import nötig; der Preis ist eine Import‑Zeile pro verwendetem Format.

Kurz gesagt: Verwenden Sie nur _ "…/register" wenn Sie über formats.Get + die Ausgabenerweiterung auflösen; das direkte Übergeben eines Konstruktors erfordert nichts Weiteres. Der leere Import ist harmlos und ist in den offiziellen Beispielen immer vorhanden.

3. Am flexibelsten: jeden Regler einzeln drehen

Wenn der Bedarf über “Datei zu Datei mit Standardeinstellungen” hinausgeht, ändern Sie die API nicht — die drei Convert‑Parameter können unabhängig voneinander ausgetauscht werden.

1. Eingabe (Quelle): von einer Datei, in den Speicher, zu jedem 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, …)

Hinweis: Die zugrunde liegende Engine benötigt zufälligen Zugriff auf den gesamten Inhalt, sodass ReaderSource den Stream beim ersten Zugriff vollständig puffert; jeder spätere Open liefert eine Kopie dieses Puffers. Verwenden Sie ihn bedenkenlos für HTTP, wissen Sie jedoch, dass es kein echtes “Konvertieren beim Streamen” ist.

2. Format (saveOption): Standardwerte oder angepasst

SzenarioSchreibstilWarum
Ein Format mit Standardeinstellungenformats.Get("pdf")nach Erweiterung gesucht; nicht groß-/kleinschreibungssensitiv; nil wenn nicht registriert
Ein Format mit format-spezifischen Optionenpdf.New(pdf.WithOnePagePerSheet(true))funktionale Optionen
Standardeinstellungen, explizites Formatooxml.New() / csv.New() / …ein null-Argument‑Konstruktor pro Format
Ein benutzerdefiniertes privates Formatformats.Register("fmt", factory)in das Register einbinden — siehe docs/register.md

Funktionale Optionen erscheinen nur bei den Formaten, die sie unterstützen: Eine Seite pro Arbeitsblatt im PDF ist WithOnePagePerSheet; Bilder inline im HTML sind WithExportImagesAsBase64; TSV ist formats.Get("tsv"). Optionen haben Zeigersemantik: Weglassen = der Standard der Engine; das explizite Übergeben von false zählt ebenfalls.

3. Ausgabe (sink): eine Datei, Bytes oder ein 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 und WriterSink sind die beiden Schlüssel zu „am flexibelsten“: Sie ermöglichen es, dass das Konvertierungsergebnis das Laufwerk vollständig überspringt und direkt in eine HTTP‑Antwort, Objektspeicher, ein Zip usw. fließt. Alle drei sind verschiedene Implementierungen desselben sink‑Parameters, sodass alles, was []byte oder einen io.Writer akzeptiert, in den Konverter eingesteckt werden kann.


4. Real-Umgebungsbeispiele: ein Convert, viele Formen

A. In-Memory konvertieren; der Download-Endpunkt streamt PDF

Das gängige serverseitige Szenario — ein Benutzer lädt xlsx hoch, der Endpunkt gibt PDF zurück, keine temporären Dateien:

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

Um stattdessen CSV / Excel / HTML auszugeben, tauschen Sie nur die saveOption aus (zum Beispiel formats.Get("csv")); sonst ändert sich nichts.

B. Einmal lesen, Batch-Export in viele Formate

Lese die Eingabe nur einmal ein und verwende sie anschließend über BytesSource für beliebig viele Ausgaben – ideal für das Exportieren jedes Berichts:

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. “Speichern unter” innerhalb der Tabellenkalkulationsfamilie (Migration / Kompatibilität)

Formatmigration innerhalb der Tabellenkalkulationsfamilie, z. B. xlsx → xls, xlsx → xlsb, xlsx → ods, und die Vorlagen xltx/xltm — immer noch ein Convert, nur die saveOption ändert sich:

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. Optionen pro Format anpassen, nur wenn nötig

Funktionale Optionen erscheinen, wenn Sie feine Kontrolle benötigen — beachten Sie, dass jeder Aufruf unten real und korrekt typisiert ist:

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

Hinweis: Für Einzelausgaben wie CSV/HTML/Image exportiert die Engine standardmäßig nur das Arbeitsblatt, das sie als „aktiv“ betrachtet. Um den Umfang zu steuern, verwenden Sie die jeweiligen Optionen des Formats – z. B. html.WithExportActiveWorksheetOnly(true) (nur das aktive Blatt?), html.WithShowAllSheets(true) oder csv.WithExportAllSheets(true). Pixelgenaue Bildqualität, die genaue Auswahl der zu rendernden Arbeitsblätter und weitere tiefgreifende Steuerungen existieren weiterhin auf Engine‑Ebene, aber das aktuelle image‑Paket stellt nur den Schalter WithImageType bereit – greifen Sie bei Bedarf auf das zugrunde liegende Binding zurück; das Toolkit wird im Laufe der Zeit weitere Optionen integrieren.

E. Batch-Konvertierung über ein Verzeichnis

Der alltägliche “diesen Ordner mit Excels in PDF konvertieren”-Job wird mit der Erweiterungsableitung sehr kurz.

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. Klassifizierung von Fehlern

Konverterfehler sind klassifizierbare Sentinel‑Fehler; kombinieren Sie sie mit errors.Is für eine präzise Handhabung:

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() gibt jede aktuell registrierte Erweiterung zurück (sortiert, nebenläufig sicher) — praktisch zum Erzeugen einer „unterstützten Formate“-Liste oder eines Fehlertipps.

5. Unterstützte Formate auf einen Blick (32 standardmäßig registriert)

FamilieAusgabe‑ErweiterungenTuning‑Paket
Excelxlsx xlsm xltx xltm xls xlsbsaveoptions/ooxml, xls, xlsb
OpenDocumentods xml dif dbfsaveoptions/ods, xml, dif, dbf
Text / Datencsv txt tsv json sqlsaveoptions/csv, txt, json, sqlScript
Dokumente / Layoutpdf docx pptx xps pcl epubsaveoptions/pdf, docx, pptx, xps, pcl, ebook
Webhtml mdsaveoptions/html, markdown
Bilderpng jpg jpeg gif bmp svg emf tif tiffsaveoptions/image

Um das Binary schlank zu halten, importieren Sie nur die Subpakete, die Sie verwenden (wie pdf); für Komfort oder generische Weiterleitung, blank-import …/register. Das Importieren eines Subpakets löst dessen Registrierung aus; Registrierungs‑Lookups sind case‑insensitive; eine nicht registrierte Erweiterung gibt nil zurück (kein Fehler), sodass die Entscheidung über das Fallback dem Aufrufer überlassen wird.


6. Best Practices und Fallstricke

  1. Ein einziger Einstiegspunkt — nicht neu erfinden. File-to-File verwendet Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); wechseln Sie die Sinks nur, wenn Sie Bytes oder einen Stream benötigen. Rollen Sie nicht selbst os.Open → ReadAll → … → os.WriteFile zusammen; die Quelle/der Sink verwalten Ressourcen und erstellen für Sie die übergeordneten Verzeichnisse.

  2. Parallelität und Lizenzierung. Die zugrunde liegende Engine ist nativ: teilen Sie nicht ein SaveOption/Workbook‑Objekt über Goroutinen im selben Prozess; für Massenverarbeitung Dateien sequenziell verarbeiten oder über Prozesse parallelisieren. Die Evaluierungs‑Build begrenzt die Ladevorgänge (etwa einhundert NewWorkbook_Stream‑Ladevorgänge pro Prozess) — bei großen Benchmarks den Prozess neu starten, um den Zähler zurückzusetzen.

  3. Lesen Sie die Eingabe einmal und verwenden Sie sie dann erneut. Um mehrere Formate aus einer Eingabe zu erzeugen, os.ReadFile Sie sie zuerst in ein []byte und konvertieren Sie wiederholt mit BytesSource, um wiederholte Festplattenlesungen und Neuladevorgänge zu vermeiden.

  4. Streams werden gepuffert. NewReaderSource puffert beim ersten Zugriff, daher eignet es sich für einmalige Eingaben wie einen HTTP‑Request‑Body, nicht für echtes Streaming sehr großer Dateien (verwenden Sie FilePathSource, das direkt auf die Festplatte zeigt, für solche Fälle).

  5. Wissen Sie, wer das Ausgabe‑Layout bestimmt. Convert ist „eine Eingabe → eine Ausgabedatei“. Wenn Sie eine Datei pro Arbeitsblatt oder ein ZIP benötigen, verwenden Sie manipulator.Split mit einem FolderSink/ZipSink — zwingen Sie es nicht über Convert.