المحول: سطر واحد في أبسط صورة، أي بيئة بأقصى مرونة لها
حزمة converter من Aspose.Cells for Go via C++ Toolkits تقلل كل حاجة “turn a spreadsheet into another format” إلى دالة إدخال واحدة:
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 أدناه.) بقية هذه المقالة تنتقل من الأكثر اختصارًا إلى الأكثر مرونة، مع توسيع المقتطف السابق في كل خطوة بدلاً من الانتقال إلى واجهة برمجة تطبيقات مختلفة.
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أدناه. بالنسبة لحالة الملف→الملف، الاثنين متساويان تمامًا — هذا كل ما يفعله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— لديك حرفيًا مساران وتكون راضيًا عن الإعدادات الافتراضية للمحرك لتنسيق الهدف. مثالي لبرامج التحويل الفردية، دفعات الترحيل، ومساعدي الاختبار. ثمنه: لا يمكن أن يغادر الإدخال أو الإخراج أو الإعدادات شكل “مسار الملف + الإعدادات الافتراضية” — وهو مهمل.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)، أو استيراد فارغ…/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، فقط اعلم أنه ليس تحويلًا حقيقيًا “convert while streaming”.
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. الإخراج (المستقبل): ملف، بايتات، أو 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، العديد من الأشكال
أ. التحويل في الذاكرة؛ نقطة النهاية للتحميل تبث 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. تصنيف الأخطاء
أخطاء المحول هي أخطاء مراقبة قابلة للتصنيف؛ قم بزوجها مع 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. أفضل الممارسات والاحتياطات
- نقطة دخول واحدة — لا تعيد اختراعها. يستخدم التحويل من ملف إلى ملف الدالة
Convert(FilePathSource, formats.Get(ext)/constructor, FilePathSink)؛ قم بتغيير الـ sinks فقط عندما تحتاج إلى بايتات أو تدفق. لا تقم بإنشاء سلسلة يدويةos.Open → ReadAll → … → os.WriteFile؛ فالمصدر/المستقبل يديران الموارد وينشئان الأدلة الأم للملف تلقائيًا. - التزامن والترخيص. المحرك الأساسي أصلي: لا تشارك كائن
SaveOption/workbook واحد عبر goroutines في نفس العملية؛ للعمليات الضخمة عالج الملفات تسلسليًا أو وزع العمل عبر عمليات متعددة. نسخة التقييم تحدّ من عدد التحميلات (حوالي مئة تحميلNewWorkbook_Streamلكل عملية) — للمعايير الكبيرة، أعد تشغيل العملية لإعادة تعيين العداد. - اقرأ الإدخال مرة واحدة، ثم أعد استخدامه. لإنتاج عدة صيغ من إدخال واحد، استخدم
os.ReadFileلقراءة الملف إلى[]byteأولاً ثم قم بالتحويل المتكرر باستخدامBytesSource، لتجنب قراءات القرص المتكررة وإعادة التحميل. - التدفقات تُخزن مؤقتًا.
NewReaderSourceيخزن مؤقتًا عند الوصول الأول، لذا فهو مناسب للمدخلات ذات الاستخدام الواحد مثل جسم طلب HTTP، وليس للبث الحقيقي للملفات الكبيرة جدًا (استخدمFilePathSourceالذي يشير مباشرة إلى القرص لهذه الحالة). - اعرف من يحدد تخطيط الإخراج.
Convertهو “إدخال واحد → ملف إخراج واحد”. إذا كنت بحاجة إلى ملف واحد لكل ورقة عمل أو ملف zip، فذلك يتم عبرmanipulator.SplitمعFolderSink/ZipSink— لا تجبره على المرور عبرConvert.
