Konwerter: jedna linia w najprostszej postaci, dowolne środowisko w najbardziej elastycznej formie
Pakiet converter biblioteki Aspose.Cells for Go via C++ Toolkits redukuje każde zapotrzebowanie na “przekształcenie arkusza kalkulacyjnego w inny format” do jednej funkcji wejściowej:
converter.Convert(source, saveOption, sink) error
Ma tylko trzy parametry, a każdy odpowiada dokładnie na jedno pytanie:
| Parametr | Typ | Pytanie, na które odpowiada | Typowe wartości |
|---|---|---|---|
source | datasource.DataSource | Skąd pochodzi wejście? | FilePathSource(path), BytesSource(data), NewReaderSource(r) |
saveOption | saveoptions.SaveOption | W jakim formacie wyjście i jak je dostroić? | formats.Get("pdf") dla domyślnych po nazwie, lub pdf.New(pdf.WithOnePagePerSheet(true)) dla precyzyjnej kontroli |
sink | datasource.DataSink | Dokąd trafia wyjście? | FilePathSink(path), &BytesSink{}, NewWriterSink(w) |
Dla najbardziej zwięzłego stylu użyj domyślnych ustawień na wszystkich trzech osiach; dla najbardziej elastycznego obróć każde pokrętło niezależnie. Ponieważ trzy osie są niezależne, to samo Convert naturalnie pasuje do środowisk plikowych, pamięciowych, HTTP i wsadowych. (Istnieje również starszy jednoliniowy kod, ConvertSpreadsheetToFile, dla prostego przypadku plik→plik — porównano w sekcji 1 poniżej.) Reszta tego artykułu przechodzi od najbardziej zwięzłego do najbardziej elastycznego, rozwijając poprzedni fragment kodu w każdym kroku zamiast przełączać się na inne API.
1. Najprostszy: jedno wywołanie dla jednego formatu
1.1 Wbudowany jednolinijkowy konwerter plik→plik: ConvertSpreadsheetToFile
Pakiet zawiera także starszy, jeszcze krótszy wpis dla klasycznego przypadku „convert this file into that file”. Wystarczy podać dwie ścieżki:
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)
}
}
Przestarzałe.
ConvertSpreadsheetToFilejest cienką nakładką zachowaną dla zachowania kompatybilności wstecznej; nowy kod jest skierowany doconverter.Convertponiżej. W przypadku file→file oba są dokładnie równoważne — to wszystko, co robiConvertSpreadsheetToFile:
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 i forma Convert powyżej dają identyczne wyniki; różni je scenariusz, jak pokazano dalej.
1.2 Zalecany jednowierszowy: Convert z ustawieniami domyślnymi
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)
}
}
Wszystkie trzy ustawienia przyjmują wartości domyślne: wejście = plik, format = pdf.New() bez żadnej konfiguracji, wyjście = plik. To jest najbardziej zwięzła forma — bez ręcznego os.Open/os.Create, bez alokacji obiektów, bez zamykania uchwytów; źródło/odbiornik zarządzają zasobami za Ciebie.
1.3 Porównanie dwóch kształtów plik→plik
ConvertSpreadsheetToFile(in, out) | converter.Convert(source, opt, sink) | |
|---|---|---|
| Wejście | tylko ścieżka do pliku | dowolny DataSource: ścieżka, []byte, lub czytnik |
| Wyjście | tylko ścieżka do pliku | dowolny DataSink: ścieżka, bajty lub io.Writer |
| Format wyjścia | zawsze wywnioskowany z rozszerzenia, tylko domyślne | dowolny saveOption: domyślny lub dostosowany do formatu |
| Dodatkowy import | wymaga zarejestrowania formatu (_ \"…/register\", lub pasującego podpakietu saveoptions/*) | brak, gdy przekazujesz konstruktor taki jak pdf.New() |
| Status | przestarzała cienka nakładka (stabilna, zachowana dla kompatybilności) | udokumentowany jedyny punkt wejścia dla nowego kodu |
Ten sam plik→wynik pliku, dwa różne scenariusze:
ConvertSpreadsheetToFile— dosłownie masz dwie ścieżki i jesteś zadowolony z domyślnych ustawień silnika dla formatu docelowego. Idealne dla jednorazowych skryptów konwersji, partii migracji i pomocników testowych. Jego koszt: wejście, wyjście i ustawienia nigdy nie mogą opuścić kształtu „file path + defaults” — i jest przestarzałe.converter.Convert— chcesz tę samą jednowierszową metodę, ale którykolwiek z trzech argumentów może być wymienny: jutro wejście będzie[]bytez przesyłki, docelowy wymagapdf.WithOnePagePerSheet(true), lub wyjście powinno trafić bezpośrednio do odpowiedzi HTTP. To dokładnie scenariusze z sekcji 3 i 4 — napisz to raz przy użyciuConvert, a każda zmiana to edycja jednego argumentu.
2. Najprostszy × N: jedna funkcja dla wszystkich 32 formatów
Następny poziom zwięzłości polega na używaniu tej samej funkcji niezależnie od tego, jaki format jest celem. Ponieważ saveOption może być obliczone z rozszerzenia pliku wyjściowego, wyciągnięcie tej inferencji na zewnątrz daje “uniwersalny konwerter plików”:
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")
}
Użycie pozostaje identyczne — zmienia się tylko rozszerzenie wyjściowe:
$ 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
Co to jest ten pusty import _ "…/register"?
Istnieją dwa sposoby uzyskania saveOption; jest to jedyny koncept, który styl „najbardziej zwięzły” musi zrozumieć:
- Sprawdź w rejestrze po nazwie formatu (
formats.Get): rozszerzenie musi być obecne w rejestrzeformats. Każdy podpakietsaveoptions/*rejestruje się w własnej funkcjiinit(), ale tylko po zaimportowaniu. Importuj potrzebne podpakiety pojedynczo (np. tylkopdf, jeśli konwertujesz do PDF), lub pusty import…/registerjednorazowo, który rejestruje wszystkie 32 formaty naraz — „uniwersalny konwerter” powyżej opiera się na tej jednej linii. - Wywołaj konstruktor bezpośrednio (
pdf.New(),markdown.New()): omija rejestr, nie jest potrzebny importregister; koszt to jedna linia importu na każdy używany format.
Krótko mówiąc: używaj _ "…/register" tylko wtedy, gdy rozwiązujesz przez formats.Get + rozszerzenie wyjściowe; przekazywanie konstruktora bezpośrednio nie wymaga nic dodatkowego. Pusty import jest nieszkodliwy i zawsze występuje w oficjalnych przykładach.
3. Najbardziej elastyczne: reguluj każde pokrętło niezależnie
Gdy potrzeba wykracza poza „plik do pliku z ustawieniami domyślnymi”, nie zmieniasz API — trzy parametry Convert można wymieniać niezależnie.
1. Wejście (źródło): z pliku, do pamięci, do dowolnego strumienia
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, …)
Uwaga: podstawowy silnik wymaga losowego dostępu do całej zawartości, więc
ReaderSourcebuforuje strumień w całości przy pierwszym dostępie; każde późniejszeOpenzwraca kopię tego bufora. Używaj go śmiało w HTTP, ale pamiętaj, że nie jest to prawdziwe “konwertowanie w trakcie strumieniowania”.
2. Format (saveOption): domyślne, lub dostosowane
| Scenariusz | Styl kodowania | Dlaczego |
|---|---|---|
| Jakiś format z ustawieniami domyślnymi | formats.Get("pdf") | wyszukiwany po rozszerzeniu; nie uwzględnia wielkości liter; nil jeśli nie jest zarejestrowany |
| Format z opcjami specyficznymi dla formatu | pdf.New(pdf.WithOnePagePerSheet(true)) | opcje funkcyjne |
| Ustawienia domyślne, wyraźny format | ooxml.New() / csv.New() / … | pojedynczy konstruktor bezargumentowy dla każdego formatu |
| Niestandardowy prywatny format | formats.Register("fmt", factory) | podłącz do rejestru — zobacz docs/register.md |
Opcje funkcjonalne pojawiają się tylko w formatach, które je posiadają: jedna strona na arkusz w PDF to WithOnePagePerSheet; obrazy wbudowane w HTML to WithExportImagesAsBase64; TSV to formats.Get("tsv"). Opcje są semantyką wskaźnikową: pominięcie = domyślne ustawienie silnika; przekazanie false explicite również się liczy.
3. Wyjście (odbiornik): plik, bajty lub 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 i WriterSink są dwoma kluczami do „najbardziej elastycznego”: pozwalają, aby wynik konwersji ominął całkowicie dysk i przepłynął bezpośrednio do odpowiedzi HTTP, magazynu obiektowego, archiwum zip i tak dalej. Wszystkie trzy są różnymi implementacjami tego samego parametru sink, więc wszystko, co akceptuje []byte lub io.Writer, może być podłączone do konwertera.
4. Przykłady z rzeczywistego środowiska: jeden Convert, wiele kształtów
A. Konwertuj w pamięci; punkt końcowy pobierania strumieniuje PDF
Typowy scenariusz po stronie serwera — użytkownik przesyła plik xlsx, endpoint zwraca PDF, bez plików tymczasowych:
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())
}
Aby zamiast tego wydać CSV / Excel / HTML, zamień tylko saveOption (na przykład formats.Get("csv")); nic innego się nie zmieni.
B. Odczytaj raz, eksportuj wsadowo do wielu formatów
Wczytaj wejście jednorazowo, a następnie użyj go ponownie poprzez BytesSource dla dowolnej liczby wyjść — idealne do “eksportu każdego raportu”:
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. “Zapisz jako” w rodzinie arkuszy kalkulacyjnych (migracja / kompatybilność)
Migracja formatu w obrębie rodziny arkuszy kalkulacyjnych, np. xlsx → xls, xlsx → xlsb, xlsx → ods, oraz szablony xltx/xltm — wciąż jedno Convert, zmienia się tylko 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. Dostosuj opcje w zależności od formatu, tylko w razie potrzeby
Opcje funkcyjne pojawiają się, gdy potrzebna jest precyzyjna kontrola — zauważ, że każde wywołanie poniżej jest rzeczywiste i poprawnie typowane:
// 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" / …
Wskazówka: w przypadku wyjść jednoplikowych, takich jak CSV/HTML/obraz, silnik domyślnie eksportuje tylko arkusz, który uznaje za „aktywny”. Aby kontrolować zakres, użyj własnych opcji formatu — np.
html.WithExportActiveWorksheetOnly(true)(tylko aktywny arkusz?),html.WithShowAllSheets(true), lubcsv.WithExportAllSheets(true). Jakość obrazu na poziomie pikseli, dokładny wybór renderowanych arkuszy oraz inne zaawansowane kontrolki nadal istnieją na warstwie silnika, ale obecny pakietimageudostępnia jedynie przełącznikWithImageType— sięgnij po podstawowe powiązanie, jeśli potrzebujesz więcej; zestaw narzędzi będzie stopniowo dodawał kolejne opcje.
E. Konwersja wsadowa w katalogu
Codzienne zadanie “przekonwertuj ten folder Excela na PDF” staje się bardzo krótkie dzięki wnioskowaniu rozszerzenia:
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. Klasyfikowanie błędów
błędy konwertera są klasyfikowalnymi błędami sentinel; połącz je z errors.Is, aby precyzyjnie je obsługiwać:
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() zwraca wszystkie aktualnie zarejestrowane rozszerzenia (posortowane, bezpieczne w środowisku wielowątkowym) — przydatne przy generowaniu listy “obsługiwanych formatów” lub podpowiedzi o błędzie.
5. Obsługiwane formaty w skrócie (32 zarejestrowane domyślnie)
| Rodzina | Rozszerzenia wyjściowe | Pakiet dostrajania |
|---|---|---|
| Excel | xlsx xlsm xltx xltm xls xlsb | saveoptions/ooxml, xls, xlsb |
| OpenDocument | ods xml dif dbf | saveoptions/ods, xml, dif, dbf |
| Tekst / dane | csv txt tsv json sql | saveoptions/csv, txt, json, sqlScript |
| Dokumenty / układ | pdf docx pptx xps pcl epub | saveoptions/pdf, docx, pptx, xps, pcl, ebook |
| Web | html md | saveoptions/html, markdown |
| Obrazy | png jpg jpeg gif bmp svg emf tif tiff | saveoptions/image |
Aby utrzymać binarkę lekką, importuj tylko podpakiety, których używasz (np. pdf); dla wygody lub ogólnego dyspozycji, użyj pustego importu …/register. Importowanie podpakietu wywołuje jego rejestrację; wyszukiwania w rejestrze są nieczułe na wielkość liter; niezarejestrowane rozszerzenie zwraca nil (nie błąd), pozostawiając decyzję o fallbacku wywołującemu.
6. Najlepsze praktyki i uwagi
- Jedno wejście — nie wymyślaj tego od nowa. File-to-file używa
Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); zmieniaj sinki tylko wtedy, gdy potrzebujesz bajtów lub strumienia. Nie twórz ręcznieos.Open → ReadAll → … → os.WriteFile; źródło/sink zarządzają zasobami i tworzą katalogi nadrzędne za Ciebie. - Współbieżność i licencjonowanie. Podstawowy silnik jest natywny: nie udostępniaj jednego obiektu
SaveOption/workbook pomiędzy goroutine’ami w tym samym procesie; przy pracy masowej przetwarzaj pliki kolejno lub równolegle w osobnych procesach. Wersja ewaluacyjna ogranicza liczbę ładowań (około stuNewWorkbook_Streamna proces) — przy dużych benchmarkach uruchom ponownie proces, aby zresetować licznik. - Wczytaj wejście raz, a potem używaj ponownie. Aby wygenerować kilka formatów z jednego wejścia, najpierw
os.ReadFileje do[]byte, a następnie konwertuj wielokrotnie przy użyciuBytesSource, unikając wielokrotnych odczytów z dysku i ponownych ładowań. - Strumienie są buforowane.
NewReaderSourcebuforuje przy pierwszym dostępie, więc nadaje się do jednorazowych wejść, takich jak ciało żądania HTTP, a nie do prawdziwego strumieniowania bardzo dużych plików (użyjFilePathSourcewskazującego bezpośrednio na dysk w takich przypadkach). - Wiedz, kto decyduje o układzie wyjścia.
Convertto “jedno wejście → jeden plik wyjściowy”. Jeśli potrzebujesz jednego pliku na arkusz lub archiwum zip, użyjmanipulator.SplitzFolderSink/ZipSink— nie wymuszaj tego przezConvert.
