Trình chuyển đổi: một dòng ở mức đơn giản nhất, bất kỳ môi trường nào ở mức linh hoạt nhất

Gói converter của Aspose.Cells for Go via C++ Toolkits giảm mọi nhu cầu “chuyển một bảng tính sang định dạng khác” thành một hàm duy nhất:

converter.Convert(source, saveOption, sink) error

Nó chỉ có ba tham số, và mỗi tham số trả lời chính xác một câu hỏi:

Tham sốKiểuCâu hỏi mà nó trả lờiGiá trị điển hình
sourcedatasource.DataSourceDữ liệu đầu vào đến từ đâu?FilePathSource(path), BytesSource(data), NewReaderSource(r)
saveOptionsaveoptions.SaveOptionĐầu ra là gì, và được tinh chỉnh như thế nào?formats.Get("pdf") cho mặc định theo tên, hoặc pdf.New(pdf.WithOnePagePerSheet(true)) để kiểm soát chi tiết
sinkdatasource.DataSinkĐầu ra sẽ được ghi tới đâu?FilePathSink(path), &BytesSink{}, NewWriterSink(w)

Đối với kiểu ngắn gọn nhất, hãy sử dụng mặc định trên cả ba trục; đối với kiểu linh hoạt nhất, hãy điều chỉnh từng nút một cách độc lập. Vì ba trục này độc lập, cùng một hàm Convert tự nhiên phù hợp với các môi trường file, memory, HTTP và batch. (Cũng có một hàm một dòng legacy, ConvertSpreadsheetToFile, cho trường hợp file→file đơn giản — được so sánh trong mục 1 bên dưới.) Phần còn lại của bài viết chuyển từ ngắn gọn nhất sang linh hoạt nhất, mở rộng đoạn mã trước đó từng bước thay vì chuyển sang API khác.


1. Cách đơn giản nhất: một lời gọi cho một định dạng

1.1 Một dòng lệnh tích hợp sẵn file→file: ConvertSpreadsheetToFile

Gói cũng cung cấp một mục nhập cũ hơn, thậm chí ngắn hơn cho trường hợp cổ điển “convert this file into that file”. Tất cả những gì cần chỉ là hai đường dẫn:

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

Đã lỗi thời. ConvertSpreadsheetToFile là một wrapper mỏng được giữ lại để tương thích ngược; mã mới được chuyển hướng tới converter.Convert bên dưới. Đối với trường hợp file→file, hai phương pháp này hoàn toàn tương đương — đây là tất cả những gì ConvertSpreadsheetToFile thực hiện:

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 và dạng Convert ở trên tạo ra kết quả giống nhau; điểm khác nhau là kịch bản, như được so sánh tiếp theo.

1.2 Câu lệnh một dòng được đề xuất: Convert với các mặc định

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

Tất cả ba tùy chọn đều sử dụng giá trị mặc định: input = một tệp, format = pdf.New() với không cấu hình nào, output = một tệp. Đó là dạng “ngắn gọn nhất” — không cần os.Open/os.Create thủ công, không cấp phát đối tượng, không đóng handle; nguồn/đích quản lý tài nguyên cho bạn.

1.3 Hai hình dạng file→file được so sánh

ConvertSpreadsheetToFile(in, out)converter.Convert(source, opt, sink)
Đầu vàochỉ đường dẫn tệpbất kỳ DataSource nào: đường dẫn, []byte, hoặc bộ đọc
Đầu rachỉ đường dẫn tệpbất kỳ DataSink nào: đường dẫn, byte, hoặc io.Writer
Định dạng đầu raluôn được suy ra từ phần mở rộng, chỉ mặc địnhbất kỳ saveOption nào: mặc định hoặc tinh chỉnh theo định dạng
Nhập khẩu bổ sungcần đăng ký định dạng (_ "…/register", hoặc subpackage saveoptions/* phù hợp)không có, khi bạn truyền một constructor như pdf.New()
Trạng tháibọc mỏng đã lỗi thời (ổn định, giữ lại để tương thích)điểm vào duy nhất đã được tài liệu hoá cho mã mới

Cùng tệp→kết quả tệp, hai kịch bản khác nhau:

  • ConvertSpreadsheetToFile — bạn thực sự chỉ có hai đường dẫn và hài lòng với các cài đặt mặc định của engine cho định dạng đích. Thích hợp cho các script chuyển đổi một lần, các lô di chuyển và các công cụ hỗ trợ kiểm thử. Giá của nó: đầu vào, đầu ra và các cài đặt không bao giờ rời khỏi dạng “file path + defaults” — và nó đã bị lỗi thời.
  • converter.Convert — bạn muốn một dòng lệnh tương tự, nhưng bất kỳ một trong ba đối số nào cũng có thể thay thế: ngày mai đầu vào có thể là []byte tải lên, mục tiêu cần pdf.WithOnePagePerSheet(true), hoặc đầu ra nên đi thẳng vào phản hồi HTTP. Đó chính là các kịch bản của mục 3 và 4 — viết một lần với Convert, và mỗi thay đổi chỉ là chỉnh sửa một đối số.

2. Cách đơn giản nhất × N: một hàm cho tất cả 32 định dạng

Cấp độ tiếp theo của sự ngắn gọn là sử dụng cùng một hàm bất kể định dạng nào bạn nhắm tới. Bởi vì saveOption có thể được tính toán từ phần mở rộng của tệp đầu ra, việc tách ra suy luận này tạo ra một “bộ chuyển đổi tệp đa năng”:

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

Cách sử dụng vẫn giống nhau — chỉ phần mở rộng đầu ra thay đổi:

$ 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

Import trống _ "…/register" là gì?

Có hai cách để lấy một saveOption; đây là khái niệm duy nhất mà phong cách “most concise” cần hiểu:

  • Tra cứu từ registry theo tên định dạng (formats.Get): phần mở rộng phải có trong registry formats. Mỗi subpackage saveoptions/* tự đăng ký trong hàm init() riêng của nó, nhưng chỉ khi được import. Import các subpackage bạn cần từng cái một (chỉ pdf, ví dụ, nếu bạn chuyển đổi sang PDF), hoặc blank-import …/register một lần, cái sẽ đăng ký tất cả 32 định dạng cùng lúc — “bộ chuyển đổi đa năng” ở trên dựa vào dòng duy nhất đó.
  • Gọi trực tiếp một constructor (pdf.New(), markdown.New()): bỏ qua registry, không cần import register; chi phí là một dòng import cho mỗi định dạng bạn sử dụng.

Tóm lại: chỉ sử dụng _ "…/register" khi bạn giải quyết thông qua formats.Get + phần mở rộng đầu ra; việc truyền trực tiếp một constructor không cần gì thêm. Việc import rỗng là vô hại và luôn xuất hiện trong các ví dụ chính thức.

3. Linh hoạt nhất: điều chỉnh từng nút một cách độc lập

Khi nhu cầu vượt ra ngoài “file to file với cài đặt mặc định”, bạn không thay đổi API — ba tham số Convert có thể hoán đổi độc lập với nhau.

1. Đầu vào (nguồn): từ tệp, vào bộ nhớ, tới bất kỳ luồng nào

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, …)

Lưu ý: công cụ nền tảng cần truy cập ngẫu nhiên vào toàn bộ nội dung, vì vậy ReaderSource lưu trữ toàn bộ luồng vào bộ đệm khi truy cập lần đầu; mỗi lần Open sau đó sẽ phục vụ một bản sao của bộ đệm đó. Hãy sử dụng nó một cách tự tin cho HTTP, chỉ cần biết rằng nó không phải là một "convert while streaming" thực sự.

2. Định dạng (saveOption): mặc định, hoặc tinh chỉnh

Kịch bảnKiểu viếtLý do
Một định dạng với cài đặt mặc địnhformats.Get("pdf")được tra cứu theo phần mở rộng; không phân biệt chữ hoa chữ thường; nil nếu chưa đăng ký
Một định dạng với các tùy chọn riêng cho định dạngpdf.New(pdf.WithOnePagePerSheet(true))các tùy chọn chức năng
Cài đặt mặc định, định dạng rõ ràngooxml.New() / csv.New() / …một hàm khởi tạo không đối số cho mỗi định dạng
Một định dạng riêng tư tùy chỉnhformats.Register("fmt", factory)cắm vào registry — xem docs/register.md

Các tùy chọn chức năng chỉ xuất hiện trên các định dạng có chúng: một trang cho mỗi worksheet trong PDF là WithOnePagePerSheet; hình ảnh nội tuyến trong HTML là WithExportImagesAsBase64; TSV là formats.Get("tsv"). Các tùy chọn có ngữ nghĩa con trỏ: bỏ qua = mặc định của engine; truyền false một cách rõ ràng cũng được tính.

3. Đầu ra (sink): một tệp, byte, hoặc một 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 và WriterSink là hai chìa khóa để “most flexible”: chúng cho phép kết quả chuyển đổi bỏ qua đĩa hoàn toàn và chảy trực tiếp vào phản hồi HTTP, lưu trữ đối tượng, file zip, và vân vân. Ba cái này đều là các triển khai khác nhau của cùng một tham số sink, vì vậy bất kỳ thứ gì chấp nhận []byte hoặc một io.Writer đều có thể được gắn vào converter.


4. Các ví dụ trong môi trường thực: một Convert, nhiều hình dạng

A. Chuyển đổi trong bộ nhớ; endpoint tải xuống truyền PDF

Kịch bản phổ biến phía máy chủ — người dùng tải lên tệp xlsx, endpoint trả về PDF, không có tệp tạm thời:

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

Để xuất ra CSV / Excel / HTML thay vì, chỉ cần hoán đổi saveOption (ví dụ formats.Get("csv")); không có gì khác thay đổi.

B. Đọc một lần, xuất hàng loạt sang nhiều định dạng

Đọc đầu vào một lần duy nhất, sau đó tái sử dụng nó qua BytesSource cho bất kỳ số lượng đầu ra nào — lý tưởng cho “export every report”:

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. “Lưu dưới dạng” trong họ bảng tính (di chuyển / tương thích)

Di chuyển định dạng trong họ bảng tính, ví dụ xlsx → xls, xlsx → xlsb, xlsx → ods, và các mẫu xltx/xltm — vẫn chỉ có một Convert, chỉ có saveOption thay đổi:

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. Tinh chỉnh các tùy chọn theo định dạng, chỉ khi cần thiết

Các tùy chọn chức năng xuất hiện khi bạn cần kiểm soát chi tiết — lưu ý rằng mọi lời gọi bên dưới đều thực tế và được gõ đúng kiểu:

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

Tip: Đối với các đầu ra dạng tệp đơn như CSV/HTML/hình ảnh, engine mặc định chỉ xuất worksheet mà nó coi là “active”. Để kiểm soát phạm vi, hãy sử dụng các tùy chọn riêng của định dạng — ví dụ html.WithExportActiveWorksheetOnly(true) (chỉ sheet đang hoạt động?), html.WithShowAllSheets(true), hoặc csv.WithExportAllSheets(true). Chất lượng hình ảnh cấp độ pixel, việc chọn chính xác các sheet sẽ được render, và các kiểm soát sâu khác vẫn tồn tại ở lớp engine, nhưng gói image hiện tại chỉ cung cấp công tắc WithImageType — hãy truy cập binding nền nếu bạn cần thêm; bộ công cụ sẽ tiếp tục gập các tùy chọn vào theo thời gian.

E. Chuyển đổi hàng loạt trên một thư mục

Công việc hàng ngày “chuyển đổi thư mục các tệp Excel sang PDF” trở nên rất ngắn gọn với việc suy luận phần mở rộng:

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. Phân loại lỗi

các lỗi chuyển đổi là các lỗi sentinel có thể phân loại; kết hợp chúng với errors.Is để xử lý chính xác:

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() trả về mọi phần mở rộng đã đăng ký hiện tại (được sắp xếp, an toàn đồng thời) — tiện lợi cho việc tạo danh sách “định dạng được hỗ trợ” hoặc gợi ý lỗi.

5. Định dạng được hỗ trợ tại một cái nhìn tổng quan (32 định dạng được đăng ký mặc định)

NhómPhần mở rộng đầu raGói tinh chỉnh
Excelxlsx xlsm xltx xltm xls xlsbsaveoptions/ooxml, xls, xlsb
OpenDocumentods xml dif dbfsaveoptions/ods, xml, dif, dbf
Văn bản / dữ liệucsv txt tsv json sqlsaveoptions/csv, txt, json, sqlScript
Tài liệu / bố cụcpdf docx pptx xps pcl epubsaveoptions/pdf, docx, pptx, xps, pcl, ebook
Webhtml mdsaveoptions/html, markdown
Hình ảnhpng jpg jpeg gif bmp svg emf tif tiffsaveoptions/image

Để giữ binary gọn nhẹ, chỉ import các subpackage bạn sử dụng (ví dụ pdf); để tiện lợi hoặc xử lý chung, có thể blank‑import …/register. Khi import một subpackage, nó sẽ kích hoạt việc đăng ký; các truy vấn registry không phân biệt chữ hoa chữ thường; một phần mở rộng chưa đăng ký sẽ trả về nil (không phải lỗi), để quyết định fallback cho người gọi.


6. Thực tiễn tốt nhất và các lưu ý

  1. Một điểm vào duy nhất — không cần tái tạo lại. File-to-file sử dụng Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink); chỉ chuyển đổi sink khi bạn cần byte hoặc một luồng. Không tự viết tay os.Open → ReadAll → … → os.WriteFile; source/sink quản lý tài nguyên và tạo thư mục cha cho bạn.
  2. Đồng thời và cấp phép. Engine nền tảng là bản địa: không chia sẻ một đối tượng SaveOption/workbook giữa các goroutine trong cùng một tiến trình; đối với công việc hàng loạt, xử lý các tệp một cách tuần tự hoặc song song hoá qua các tiến trình. Bản build đánh giá giới hạn số lần tải (khoảng một trăm lần tải NewWorkbook_Stream mỗi tiến trình) — đối với các benchmark lớn, khởi động lại tiến trình để đặt lại bộ đếm.
  3. Đọc đầu vào một lần, sau đó tái sử dụng. Để xuất ra nhiều định dạng từ một đầu vào, trước tiên os.ReadFile nó vào một []byte và chuyển đổi lặp lại bằng BytesSource, tránh việc đọc đĩa và tải lại nhiều lần.
  4. Luồng được đệm. NewReaderSource đệm khi truy cập lần đầu, vì vậy nó phù hợp với các đầu vào một lần như thân yêu cầu HTTP, không phải streaming thực sự của các tệp rất lớn (sử dụng FilePathSource trỏ trực tiếp tới đĩa cho những trường hợp đó).
  5. Biết ai quyết định bố cục đầu ra. Convert là “một đầu vào → một tệp đầu ra”. Nếu bạn cần một tệp cho mỗi worksheet hoặc một zip, đó là manipulator.Split với FolderSink/ZipSink — không ép buộc nó qua Convert.