Конвертер: одна строка в самом простом виде, любая среда в самой гибкой форме
Пакет converter из Aspose.Cells for Go via C++ Toolkits сводит каждую необходимость “преобразовать электронную таблицу в другой формат” к одной функции входа:
converter.Convert(source, saveOption, sink) error
У него только три параметра, и каждый отвечает ровно на один вопрос:
| Параметр | Тип | Вопрос, на который отвечает | Типичные значения |
|---|---|---|---|
source | datasource.DataSource | Откуда поступает ввод? | FilePathSource(path), BytesSource(data), NewReaderSource(r) |
saveOption | saveoptions.SaveOption | Вывод в каком виде, как настроено? | formats.Get("pdf") для значений по умолчанию по имени, или pdf.New(pdf.WithOnePagePerSheet(true)) для тонкой настройки |
sink | datasource.DataSink | Куда направляется вывод? | FilePathSink(path), &BytesSink{}, NewWriterSink(w) |
Для самого лаконичного стиля используйте значения по умолчанию по всем трем осям; для самого гибкого — регулируйте каждый параметр независимо. Поскольку три оси независимы, один и тот же Convert естественно подходит для файловых, оперативных, HTTP и пакетных сред. (Существует также устаревший однострочник ConvertSpreadsheetToFile для простого случая файл→файл — сравнение в разделе 1 ниже.) Остальная часть этой статьи переходит от самого лаконичного к самому гибкому, постепенно расширяя предыдущий фрагмент на каждом шаге, а не переключаясь на другой API.
1. Самый простой: один вызов для одного формата
1.1 Встроенный однострочник file→file: ConvertSpreadsheetToFile
Пакет также поставляется со старой, ещё более короткой записью для классического случая “конвертировать этот файл в тот файл”. Всё, что нужно — два пути:
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)
}
}
Устарело.
ConvertSpreadsheetToFile— это тонкая оболочка, сохраняемая для обратной совместимости; новый код следует направлять кconverter.Convertниже. Для случая file→file оба эквивалентны — это всё, что делает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 и форма Convert, приведённые выше, дают одинаковые результаты; различие заключается в сценарии, как показано далее.
1.2 Рекомендуемый однострочник: Convert со значениями по умолчанию
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)
}
}
Все три параметра используют значения по умолчанию: input = файл, format = pdf.New() без какой‑либо конфигурации, output = файл. Это “самый лаконичный” вариант — без ручного os.Open/os.Create, без выделения объектов, без закрытия дескрипторов; источник/приёмник управляют ресурсами за вас.
1.3 Сравнение двух форм file→file
ConvertSpreadsheetToFile(in, out) | converter.Convert(source, opt, sink) | |
|---|---|---|
| Ввод | только путь к файлу | любой DataSource: путь, []byte, или reader |
| Вывод | только путь к файлу | любой DataSink: путь, байты или io.Writer |
| Формат вывода | всегда определяется по расширению, только значения по умолчанию | любой saveOption: значение по умолчанию или настройка, специфичная для формата |
| Дополнительный импорт | требуется регистрация формата (_ \"…/register\", или соответствующий подпакет saveoptions/*) | нет, если передать конструктор, например pdf.New() |
| Состояние | устаревшая тонкая обёртка (стабильный, оставлен для совместимости) | задокументированная единственная точка входа для нового кода |
Один и тот же файл → результат файла, два разных сценария:
ConvertSpreadsheetToFile— у вас буквально два пути, и вы довольны настройками по умолчанию движка для целевого формата. Идеально подходит для одноразовых скриптов конвертации, миграционных пакетов и тестовых вспомогательных средств. Его цена: ввод, вывод и настройки никогда не могут выйти за рамки формы «file path + defaults» — и он устарел.converter.Convert— вы хотите тот же однострочник, но любой из трёх аргументов может быть заменён: завтра ввод будет[]byteзагрузки, цель потребуетpdf.WithOnePagePerSheet(true), или вывод должен сразу попасть в HTTP‑ответ. Это именно сценарии разделов 3 и 4 — напишите один раз сConvert, и каждое изменение будет правкой одного аргумента.
2. Самый простой × N: одна функция для всех 32 форматов
Следующий уровень лаконичности — использование одной и той же функции независимо от того, какой формат вы выбираете. Поскольку saveOption можно вычислить из расширения выходного файла, вынесение этого вывода наружу дает «универсальный файловый конвертер»:
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")
}
Использование остается идентичным — меняется только расширение выходного файла:
$ 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
Что это за пустой импорт _ "…/register"?
Существует два способа получить saveOption; это единственная концепция, которую необходимо понять стилю «самый лаконичный».
- Найдите его в реестре по имени формата (
formats.Get): расширение должно присутствовать в реестреformats. Каждый подпакетsaveoptions/*регистрирует себя в собственномinit(), но только при импорте. Импортируйте необходимые подпакеты по одному (например, толькоpdf, если вы конвертируете в PDF), или blank-import…/registerодин раз, который регистрирует все 32 формата сразу — вышеупомянутый «универсальный конвертер» опирается на эту единственную строку. - Вызовите конструктор напрямую (
pdf.New(),markdown.New()): обходится без реестра, импортregisterне требуется; стоимость — одна строка импорта на каждый используемый формат.
Вкратце: используйте _ "…/register" только когда вы разрешаете через formats.Get + расширение вывода; передача конструктора напрямую ничего не требует. Пустой импорт безвреден и всегда присутствует в официальных примерах.
3. Самый гибкий: регулируйте каждый параметр независимо
Когда потребность выходит за рамки «файл в файл с настройками по умолчанию», вы не меняете API — три параметра Convert можно менять независимо друг от друга.
1. Ввод (источник): из файла, в память, в любой поток
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, …)
Примечание: базовый движок требует произвольного доступа ко всему содержимому, поэтому
ReaderSourceполностью буферизует поток при первом доступе; каждый последующийOpenпредоставляет копию этого буфера. Используйте его уверенно для HTTP, но имейте в виду, что это не настоящий “конвертировать во время потоковой передачи”.
2. Формат (saveOption): по умолчанию, или настроенный
| Сценарий | Стиль записи | Почему |
|---|---|---|
| Some format with default settings | formats.Get("pdf") | поиск по расширению; без учёта регистра; nil, если не зарегистрирован |
| A format with format-specific options | pdf.New(pdf.WithOnePagePerSheet(true)) | функциональные опции |
| Default settings, explicit format | ooxml.New() / csv.New() / … | один конструктор без аргументов для каждого формата |
| A custom private format | formats.Register("fmt", factory) | подключить к реестру — см. docs/register.md |
Функциональные параметры появляются только в тех форматах, которые их поддерживают: одна страница на лист в PDF — WithOnePagePerSheet; встроенные изображения в HTML — WithExportImagesAsBase64; TSV — formats.Get("tsv"). Параметры имеют семантику указателя: опускание = значение по умолчанию движка; явная передача false также считается.
3. Вывод (sink): файл, байты или 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 и WriterSink — два ключа к «наиболее гибкому»: они позволяют результату конвертации полностью обходить диск и направлять его напрямую в HTTP‑ответ, объектное хранилище, zip‑архив и т.д. Все три являются разными реализациями одного и того же параметра sink, поэтому любой объект, принимающий []byte или io.Writer, может быть подключён к конвертеру.
4. Примеры из реальной среды: один Convert, много фигур
A. Конвертация в памяти; конечная точка загрузки передаёт PDF в виде потока
Обычный сценарий на стороне сервера — пользователь загружает xlsx, эндпоинт возвращает PDF, без временных файлов:
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())
}
Чтобы выдавать CSV / Excel / HTML вместо этого, замените только saveOption (например formats.Get("csv")); больше ничего не меняется.
B. Прочитать один раз, пакетный экспорт в различные форматы
Прочитайте ввод один раз, затем переиспользуйте его через BytesSource для любого количества выводов — идеально для «экспортировать каждый отчет»:
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. “Сохранить как” в семействе электронных таблиц (миграция / совместимость)
Миграция форматов внутри семейства электронных таблиц, например xlsx → xls, xlsx → xlsb, xlsx → ods, и шаблоны xltx/xltm — всё ещё один Convert, только 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. Настройка параметров для каждого формата, только при необходимости
Функциональные опции появляются, когда требуется тонкий контроль — обратите внимание, каждый вызов ниже является реальным и правильно типизированным:
// 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" / …
Совет: для однофайловых выводов, таких как CSV/HTML/изображение, движок экспортирует только лист, который он считает «активным» по умолчанию. Чтобы управлять областью, используйте собственные параметры формата — например
html.WithExportActiveWorksheetOnly(true)(только активный лист?),html.WithShowAllSheets(true)илиcsv.WithExportAllSheets(true). Пиксельное качество изображения, точный выбор листов для рендеринга и другие глубокие настройки всё ещё доступны на уровне движка, но текущий пакетimageраскрывает только переключательWithImageType— обратитесь к нижележащей привязке, если нужно больше; со временем набор опций будет расширяться.
E. Пакетное преобразование каталога
Повседневная задача «конвертировать эту папку Excel в PDF» становится очень короткой благодаря определению расширения:
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. Классификация ошибок
Ошибки конвертера являются классифицируемыми sentinel‑ошибками; используйте errors.Is для точной обработки:
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() возвращает все текущие зарегистрированные расширения (отсортированные, потокобезопасные) — удобно для создания списка “поддерживаемых форматов” или подсказки об ошибке.
5. Поддерживаемые форматы в обзоре (32 зарегистрировано по умолчанию)
| Семейство | Выходные расширения | Пакет настройки |
|---|---|---|
| Excel | xlsx xlsm xltx xltm xls xlsb | saveoptions/ooxml, xls, xlsb |
| OpenDocument | ods xml dif dbf | saveoptions/ods, xml, dif, dbf |
| Текст / данные | csv txt tsv json sql | saveoptions/csv, txt, json, sqlScript |
| Документы / макет | pdf docx pptx xps pcl epub | saveoptions/pdf, docx, pptx, xps, pcl, ebook |
| Веб | html md | saveoptions/html, markdown |
| Изображения | png jpg jpeg gif bmp svg emf tif tiff | saveoptions/image |
Чтобы бинарный файл оставался небольшим, импортируйте только те подпакеты, которые используете (например, pdf); для удобства или общего диспетчерования используйте пустой импорт …/register. Импорт подпакета инициирует его регистрацию; поиск в реестре нечувствителен к регистру; неподдерживаемое расширение возвращает nil (не ошибку), оставляя решение о резервном варианте за вызывающим кодом.
6. Лучшие практики и предостережения
- Одна точка входа — не изобретайте её заново. File-to-file использует
Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); переключайте sink’ы только когда нужны байты или поток. Не реализуйте вручнуюos.Open → ReadAll → … → os.WriteFile; source/sink управляют ресурсами и создают родительские каталоги за вас. - Параллелизм и лицензирование. Подлежащий движок нативный: не делитесь одним объектом
SaveOption/workbook между goroutine’ами в одном процессе; для массовой обработки обрабатывайте файлы последовательно или распараллеливайте их между процессами. Оценочная сборка ограничивает количество загрузок (примерно сто загрузокNewWorkbook_Streamна процесс) — для больших тестов перезапускайте процесс, чтобы сбросить счётчик. - Прочитайте ввод один раз, а затем переиспользуйте его. Чтобы вывести несколько форматов из одного ввода, сначала
os.ReadFileего в[]byteи многократно конвертируйте с помощьюBytesSource, избегая повторных чтений с диска и перезагрузок. - Потоки буферизуются.
NewReaderSourceбуферизует данные при первом доступе, поэтому подходит для одноразовых вводов, таких как тело HTTP‑запроса, а не для реального потокового чтения очень больших файлов (для этого используйтеFilePathSource, указывающий напрямую на диск). - Знайте, кто определяет структуру вывода.
Convert— это «один ввод → один файл вывода». Если вам нужен один файл на лист или zip, используйтеmanipulator.SplitсFolderSink/ZipSink— не принуждайте это черезConvert.
