Конвертер: один рядок у найпростіший спосіб, будь-яке середовище у найгнучкішій формі

Пакет converter Aspose.Cells for Go via C++ Toolkits зменшує кожну потребу «перетворити електронну таблицю в інший формат» до однієї функції входу:

converter.Convert(source, saveOption, sink) error

Він має лише три параметри, і кожен відповідає саме на одне запитання:

ПараметрТипПитання, на яке відповідаєТипові значення
sourcedatasource.DataSourceЗвідки надходить вхід?FilePathSource(path), BytesSource(data), NewReaderSource(r)
saveOptionsaveoptions.SaveOptionУ який формат виводити, як налаштувати?formats.Get("pdf") для типових значень за назвою, або pdf.New(pdf.WithOnePagePerSheet(true)) для тонкого налаштування
sinkdatasource.DataSinkКуди йде вихід?FilePathSink(path), &BytesSink{}, NewWriterSink(w)

Для найбільш стислого стилю використовуйте значення за замовчуванням на всіх трьох осях; для найбільш гнучкого — налаштовуйте кожен параметр окремо. Оскільки три осі незалежні, той самий Convert природно підходить для файлових, пам’яті, HTTP та пакетних середовищ. (Існує також застарілий однорядковий виклик ConvertSpreadsheetToFile для простого випадку файл→файл — порівняно в розділі 1 нижче.) Решта статті переходить від найбільш стислого до найбільш гнучкого, поступово розширюючи попередній фрагмент на кожному кроці, а не переключаючись на інший API.

1. Найпростіший: один виклик для одного формату

1.1 Вбудований однорядковий файл→файл: ConvertSpreadsheetToFile

Пакет також постачається зі старішим, ще коротшим записом для класичного “convert this file into that file” випадку. Все, що потрібно, — це два шляхи:

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, або читач
Вихідтільки шлях до файлубудь-який 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; це єдина концепція, яку має розуміти стиль “most concise”.

  • Шукайте в реєстрі за назвою формату (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): за замовчуванням або налаштований

СценарійСтиль написанняЧому
Деякий формат з налаштуваннями за замовчуваннямformats.Get("pdf")шукається за розширенням; нечутливий до регістру; nil, якщо не зареєстровано
Формат зі специфічними для формату параметрамиpdf.New(pdf.WithOnePagePerSheet(true))функціональні опції
Налаштування за замовчуванням, явний форматooxml.New() / csv.New() / …один конструктор без аргументів для кожного формату
Користувацький приватний формат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. “Зберегти як” у сім’ї електронних таблиць (мiграція / сумісність)

Міграція форматів у межах сімейства електронних таблиць, наприклад 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/image, движок за замовчуванням експортує лише лист, який він вважає “активним”. Щоб керувати областю, використовуйте власні параметри формату — напр., html.WithExportActiveWorksheetOnly(true) (тільки активний лист?), html.WithShowAllSheets(true), або csv.WithExportAllSheets(true). Якість зображень на рівні пікселів, точний вибір листів, які рендеряться, та інші глибокі налаштування все ще існують на рівні движка, проте поточний пакет image відкриває лише перемикач WithImageType — звертайтеся до нижчого рівня прив’язки, якщо потрібно більше; інструментарій буде поступово додавати нові параметри.

E. Пакетне перетворення каталогу

Повсякденне “convert this folder of Excels to 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 зареєстровано за замовчуванням)

Сім’яВихідні розширенняПакет налаштувань
Excelxlsx xlsm xltx xltm xls xlsbsaveoptions/ooxml, xls, xlsb
OpenDocumentods xml dif dbfsaveoptions/ods, xml, dif, dbf
Текст / даніcsv txt tsv json sqlsaveoptions/csv, txt, json, sqlScript
Документи / макетpdf docx pptx xps pcl epubsaveoptions/pdf, docx, pptx, xps, pcl, ebook
Вебhtml mdsaveoptions/html, markdown
Зображенняpng jpg jpeg gif bmp svg emf tif tiffsaveoptions/image

Щоб зберегти бінарний файл компактним, імпортуйте лише підпакети, які ви використовуєте (наприклад pdf); для зручності або загального диспетчеризації використовуйте порожній імпорт …/register. Імпорт підпакету ініціює його реєстрацію; пошуки в реєстрі нечутливі до регістру; незареєстроване розширення повертає nil (не помилку), залишаючи рішення про резервний варіант викликаючому.

6. Кращі практики та підводні камені

  1. Один вхідний пункт — не винаходьте заново. File-to-file використовує Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); перемикайте sink лише коли потрібні байти або потік. Не робіть вручну os.Open → ReadAll → … → os.WriteFile; source/sink керують ресурсами і створюють батьківські каталоги за вас.

  2. Конкурентність і ліцензування. Базовий движок є нативним: не діліться одним об’єктом SaveOption/workbook між goroutine в одному процесі; для масової обробки обробляйте файли послідовно або паралельно в різних процесах. Оцінкова збірка обмежує кількість завантажень (близько сто NewWorkbook_Stream завантажень на процес) — для великих тестів перезапустіть процес, щоб скинути лічильник.

  3. Прочитайте вхід один раз, а потім використовуйте його повторно. Щоб вивести кілька форматів з одного входу, спочатку os.ReadFile його у []byte і конвертуйте повторно за допомогою BytesSource, уникаючи повторних читань з диска та перезавантажень.

  4. Потоки буферизуються. NewReaderSource буферизується при першому доступі, тому підходить для одноразових входів, таких як тіло HTTP‑запиту, а не для справжнього потокового оброблення дуже великих файлів (для цього використовуйте FilePathSource, який вказує безпосередньо на диск).

  5. Знайте, хто визначає макет виходу. Convert — це «один вхід → один вихідний файл». Якщо потрібен один файл на лист або zip, використовуйте manipulator.Split з FolderSink/ZipSink — не примушуйте це через Convert.