مبدل: یک خط در سادهترین حالت، هر محیط در بیشترین انعطافپذیری
بسته 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
این بسته همچنین یک ورودی قدیمیتر و حتی کوتاهتر برای حالت کلاسیک “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یک wrapper نازک است که برای سازگاری با نسخههای قبلی حفظ شده است؛ کد جدید به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)
}
}
تمامی سه تنظیم مقدار پیشفرض خود را میگیرند: ورودی = یک فایل، قالب = pdf.New() بدون هیچ پیکربندی، خروجی = یک فایل. این «most concise» شکل است — بدون os.Open/os.Create دستی، بدون تخصیص شیء، بدون بستن دسته؛ منبع/مقصد منابع را برای شما مدیریت میکند.
1.3 مقایسه دو شکل فایل→فایل
ConvertSpreadsheetToFile(in, out) | converter.Convert(source, opt, sink) | |
|---|---|---|
| ورودی | فقط مسیر فایل | هر DataSource: مسیر، []byte یا خواننده |
| خروجی | فقط مسیر فایل | هر DataSink: مسیر، بایتها یا io.Writer |
| قالب خروجی | همیشه بر اساس پسوند استخراج میشود، فقط پیشفرضها | هر saveOption: پیشفرض یا تنظیمات خاص فرمت |
| واردات اضافی | نیاز به ثبت فرمت دارد (_ "…/register", یا زیرپکیج منطبق saveoptions/*) | هیچکدام، وقتی سازندهای مانند pdf.New() را پاس میدهید |
| وضعیت | پوشش نازک منسوخ شده (پایدار، برای سازگاری نگهداشته شده) | نقطه ورودی تک مستند برای کد جدید |
فایل یکسان→نتیجه فایل، دو سناریوی متفاوت:
ConvertSpreadsheetToFile— در واقع دو مسیر دارید و از تنظیمات پیشفرض موتور برای قالب هدف راضی هستید. برای اسکریپتهای تبدیل یکبار استفاده، دستههای مهاجرت و ابزارهای تست ایدهآل است. هزینه آن این است که ورودی، خروجی و تنظیمات هرگز نمیتوانند از شکل «مسیر فایل + پیشفرضها» خارج شوند — و این روش منسوخ شده است.converter.Convert— میخواهید همان یکخطی را داشته باشید، اما هر یک از سه آرگومان قابل جایگزینی باشد: فردا ورودی یک[]byteآپلود باشد، هدف بهpdf.WithOnePagePerSheet(true)نیاز داشته باشد، یا خروجی مستقیماً به یک پاسخ HTTP برود. اینها دقیقاً سناریوهای بخشهای ۳ و ۴ هستند — یکبار باConvertبنویسید و هر تغییر تنها یک ویرایش آرگومان باشد.
2. سادهترین × N: یک تابع برای تمام ۳۲ فرمت
سطح بعدی خلاصهگویی استفاده از همان تابع است، بدون توجه به قالب هدف. چون saveOption میتواند از پسوند فایل خروجی محاسبه شود، استخراج این استنتاج منجر به یک “universal file converter” میشود:
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
این import خالی _ "…/register" چیست؟
دو روش برای به دست آوردن یک saveOption وجود دارد؛ این تنها مفهومی است که سبک “مختصرترین” باید درک کند:
- از رجیستری بر اساس نام فرمت جستجو کنید (
formats.Get): پسوند باید در رجیستریformatsموجود باشد. هر زیرپکیجsaveoptions/*خود را درinit()مخصوص خود ثبت میکند، اما فقط زمانی که وارد شود. زیرپکیجهای مورد نیاز خود را یکییکی وارد کنید (مثلاً فقطpdfاگر به PDF تبدیل میکنید)، یا یکبار…/registerرا به صورت خالی وارد کنید، که تمام ۳۲ فرمت را یکباره ثبت میکند — «مبدل جامع» بالا به آن یک خط تکی وابسته است. - بهصورت مستقیم سازندهای را فراخوانی کنید (
pdf.New(),markdown.New()): از رجیستری عبور میکند، نیازی به وارد کردنregisterنیست؛ هزینه یک خط وارد کردن برای هر فرمت مورد استفاده است.
بهطور خلاصه: فقط وقتی که از طریق formats.Get + پسوند خروجی حل میکنید، از _ "…/register" استفاده کنید؛ پاس دادن یک سازنده بهصورت مستقیم نیازی به چیز اضافی ندارد. وارد کردن خالی بیضرر است و همیشه در مثالهای رسمی موجود است.
3. انعطافپذیرترین: هر دکمه را بهصورت مستقل بچرخانید
وقتی نیاز فراتر از “file to file with default settings” میرود، شما 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 استفاده کنید، فقط بدانید که این یک «تبدیل در حین پخش» واقعی نیست.
۲. قالب (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. “ذخیره به عنوان” در خانوادهٔ صفحهگسترده (مهاجرت / سازگاری)
مهاجرت فرمت درون خانوادهٔ صفحهگسترده، به عنوان مثال 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. تبدیل دستهای در یک پوشه
کار روزمره “این پوشه از فایلهای اکسل را به 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 بهطور پیشفرض ثبت شده)
| خانواده | پسوندهای خروجی | بسته تنظیمات |
|---|---|---|
| اکسل | 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. بهترین روشها و نکات مهم
یک نقطه ورودی — دوباره اختراعش نکنید. در حالت فایل‑به‑فایل از
Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink)استفاده میشود؛ فقط زمانی که به بایتها یا یک جریان نیاز دارید، سینکها را تغییر دهید.os.Open → ReadAll → … → os.WriteFileرا بهصورت دستی ننویسید؛ منبع/سینک منابع را مدیریت میکند و برای شما پوشههای والد را ایجاد میکند.همزمانی و مجوزدهی. موتور زیرین بومی است: نکنید یک شیء
SaveOption/workbook را بین goroutineها در همان فرآیند بهاشتراک بگذارید؛ برای کارهای حجیم، فایلها را بهصورت متوالی پردازش کنید یا پردازشها را بین فرآیندها موازی کنید. نسخه ارزیابی بارگذاریها را محدود میکند (حدود صد بارNewWorkbook_Streamدر هر فرآیند) — برای بنچمارکهای بزرگ، فرآیند را مجدداً راهاندازی کنید تا شمارنده بازنشانی شود.ورودی را یکبار بخوانید، سپس دوباره استفاده کنید. برای تولید چندین فرمت از یک ورودی، ابتدا با
os.ReadFileآن را به یک[]byteتبدیل کنید و سپس بهصورت مکرر باBytesSourceتبدیل کنید، تا از خواندن مکرر دیسک و بارگذاریهای دوباره جلوگیری شود.جریانها بافر میشوند.
NewReaderSourceدر اولین دسترسی بافر میکند، بنابراین برای ورودیهای یکبار مصرف مانند بدنه درخواست HTTP مناسب است، نه جریان واقعی فایلهای بسیار بزرگ (برای این موارد ازFilePathSourceکه مستقیماً به دیسک اشاره میکند استفاده کنید).بدانید چه کسی چیدمان خروجی را تعیین میکند.
Convertبه معنای «یک ورودی → یک فایل خروجی» است. اگر به یک فایل برای هر برگه کاری یا یک zip نیاز دارید، ازmanipulator.Splitهمراه باFolderSink/ZipSinkاستفاده کنید — آن را از طریقConvertمجبور نکنید.
