תוסף ממיר

המרת מסמכים ל-45 פורמטים יעד

תוסף הממיר הופך את Doconut לשירות המרת מסמכים. הוא תורם את המנוע מאחורי הממשק הציבורי DocumentConverter, ו‑— באפשרותך לבחור — וידג'ט נשלף עם חוזה HTTP משלו, כך שאתה יכול להמיר מסמכים מ‑C#, מהווידג'ט, או מממשק משתמש שאתה בונה בעצמך.

התקנת החבילה

התקן את תוסף הממיר היציב האחרון:

bash
dotnet add package Doconut.NETStandard.Converter

כדי לעגן את התוסף לגרסה 26.8.0 הנוכחית, העבר את הגרסה בנפרד:

bash
dotnet add package Doconut.NETStandard.Converter --version 26.8.0

שמור על חבילת הממיר באותה גרסה כמו Doconut.NETStandard. מזהה החבילה הוא Doconut.NETStandard.Converter; .26.8.0 מופיע רק בשם הקובץ שהורד .nupkg.

רישום התוסף

אין שיטה AddConverter() — מודל התוספים של Doconut אחיד. כל תוסף, כולל הממיר, נרשם באותו אופן: קרא AddPlugin<TPlugin>() בתוך AddDoconut(). ConverterPlugin מגיע בחבילת NuGet משלו, Doconut.NETStandard.Converter, מותקן לצד חבילת הצופה הבסיסית.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
    options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
});

הקריאה הזו זורקת שגיאה בהפעלה במקרה של רישיון חסר, קובץ TRIAL מיושן, או רישיון שאינו זמני שאינו מעניק את היכולת Converter — InvalidOperationException שנוצר מבפנים של AddDoconut(), לפני שהאפליקציה מגישה בקשות. רישומי Demo/NFR זמניים מתקבלים; לאחר פקיעת תוקפם, ההמרה נשארת זמינה עם פלט מסומן במים. אין רמת חינם שקטה. ראה הגדרת רישיון לקבלת מידע על איך רישיונות נטענים.

המרה מ‑C#

כל המרה מחזירה MemoryStream שניתן לחיפוש וממוקם ב‑0, מוכן לקריאה או העתקה מיידית. קבל את DocumentConverter מ‑DI בכל מקום שבו אתה צריך אותו — הוא חסר מצב לפי תכנון, ולכן ניתן להשתמש במופע יחיד בבטחה לאורך בקשות.

viewer.Converter.GetConverter() אינו קיים כאן. ההמרה כבר לא נגישה דרך מופע הצופה: הזרק Doconut.DocumentConverter במקום זאת.

csharp
// הזרק DocumentConverter; הבנאי שלו פנימי, ולכן לעולם אל `new` אותו.
Stream pdf = await converter.ConvertAsync("contract.docx", ConversionTarget.Pdf, ct: ct);
csharp
// sourceExtension כולל את הנקודה המקדימה. password הוא null אלא אם המסמך מוגן.
Stream png = await converter.ConvertAsync(upload, ".xlsx", ConversionTarget.Png, password: null, ct: ct);
csharp
Stream html = await converter.WordToHtmlAsync("report.docx", ct);
csharp
Stream docx = await converter.HtmlToWordAsync(html, ConversionTarget.Docx, ct);

שתיים פרטים שקל לטעות בהם: sourceExtension על עומס ה‑stream חייב לכלול את הנקודה המקדימה (".xlsx", ולא "xlsx") — הממיר משווה זאת מול קטלוג הפורמטים והרחבה ללא נקודה לא תיפתר. ולמרות שמו, WordToHtmlAsync מחזיר Task<Stream>, ולא Task<string> — אתה מקבל את מסמך ה‑HTML (תמונות משובצות כ‑Base64) כ‑stream, בדיוק כמו כל תוצאה אחרת של המרה.

פורמטים יעד

ConversionTarget (מרחב שם Doconut.Configs.Conversion) מכיל 45 חברים:

text
Pdf, Docx, Doc, Html, Xlsx, Pptx, Png, Jpeg, Csv, Tiff, Bmp, Gif, Svg, Xml,
Txt, Xls, Jp2, Rtf, Odt, Ods, Odp, Epub, Xps, Webp, Msg, Eml, Emlx, Mhtml,
Xlsm, Xlsb, Ppt, Docm, Dot, Dotx, Dotm, Vsdx, Vssx, Vstx, Vsdm, Vssm, Vstm,
Vdx, Vsx, Vtx, Ico

Enum זה מכסה כל משפחת מקור. הספרייה הקודמת הייתה בעלת סוג *ConfigConverter לכל משפחה — WordConfigConverter, ExcelConfigConverter, וכן הלאה — כל אחד עם Enum מקונן EnTargetFormat משלו, בנוסף ל‑Enum SourceFormatConverter לקלט. כולם נעלמו; ראה את מדריך ההגירה.

לא כל מקור מומר לכל יעד — התוסף ממפה כל משפחת פורמט של מקור (Word, Excel, PowerPoint, PDF, CAD, Image, Email, Diagram, Project/Task, PSD, מסמך אינטרנט) למערכת קבועה של יעדים מותרים. אל תקודד קבוע את ה‑Enum הזה כרשימת היעדים של הממשק שלך: ?convert=open מחזיר את ה‑allowedTargets האמיתי עבור כל קובץ שהועלה זה עתה, וזה מה שצריך להניע את בוחר היעדים.

ווידג'ט נשלף

הקצוות ?convert=open|run|download של הווידג'ט הם אופציונליים ומושבתים כברירת מחדל — מאובטחים כברירת מחדל. אפשר אותם בצד השרת, יחד עם רישום הפלאגין:

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
    options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
    options.AddConverterWidget(widget =>
    {
        widget.MaxUploadMb = 25;
    });
});
html
<div id="doconut-convert"></div>
<script src="/doconut-res/js/doconutConverter.js"></script>
<script>
  Doconut.convert('#doconut-convert', { basePath: '/doconut', resPath: '/doconut-res', maxUploadMb: 25 });
</script>

בלי AddConverterWidget(), שלושת הקצוות ?convert= משיבים 404 — אך קובץ ה‑JS עצמו עדיין מוגש בכל מקרה (זהו משאב סטטי משובץ פשוט; רק הקצוות שהוא מתקשר אליהם מוגבלים). AddConverterWidget() עדיין דורש שהפלאגין Converter יהיה רשום ורישיון שמעניק Converter — הוא אינו מעניק זכויות המרה בעצמו.

התאמת הווידג'ט

אפשרויות אתחול מועברות ל‑Doconut.convert(selector, options):

אפשרותסוגברירת מחדלהערות
basePathstring/doconutנתיב בסיסי עבור הקצוות ?convert=; הוא חייב להתאים לענף ASP.NET שבו UseDoconut() מותקן בפועל (בדרך כלל מתואם דרך MiddlewarePath)
resPathstring/doconut-resמתקבל לצורך עקביות תצורה עם וידג'טים אחרים של Doconut; וידג'ט הממיר כרגע אינו בונה URL ממנו
maxUploadMbnumber25בדיקה מקדימה בצד הלקוח בלבד — דוחה קובץ גדול מדי לפני ההעלאה. השרת אוכף מגבלה משלו באופן עצמאי ומשיב 413 אם היא חורגת
licenseUrlstring | nullnullכאשר מוגדר, הופך את הודעת סימן המים על מסך התוצאה לקישור ל‑URL זה
labelsobject{}עוקף כל תת‑קבוצה של מחרוזות ברירת המחדל באנגלית של הווידג'ט (טקסט גרירה, כפתורים, הודעות aria‑live, הודעות שגיאה)

קריאות חזרה:

קריאה חזרהמתרחשת כאשרנתונים
onReady()הווידג'ט רינדד את מסך ההמתנה/גרירה שלו—
onSourceLoaded({ token, pages, sourceExt, allowedTargets })?convert=open מצליחאסימון סשן מקור, מספר עמודים, סיומת מקור (ללא נקודה מקדימה), רשימת יעדים מותרים
onConverted({ downloadToken, resultToken, resultPages, downloadName, watermarked, target })?convert=run מצליחאותם השדות כמו בתגובת הריצה, בנוסף ל‑target שנדרש
onDownload({ downloadName, downloadToken })המשתמש לוחץ על קישור ההורדהמתרחש יחד עם ההורדה הטבעית של הדפדפן — הוא אינו חוצץ או מחליף אותה
onError({ phase, message })בקשת פתיחה או ריצה נכשלתphase הוא 'open' או 'run'; message הוא השגיאה המסוננת של השרת (או הודעת צד‑לקוח לבדיקת גודל העלאה)

Doconut.convert() מחזיר את מופע הווידג'ט עצמו — שמור עליו כדי לשלוט בווידג'ט תכנותית:

javascript
const conv = Doconut.convert('#doconut-convert', { basePath: '/doconut' });
conv.reset();         // חזרה למסך ההמתנה/הפלה; לא מפעיל מחדש את onReady
conv.loadFile(file);  // מתחיל את הזרימה עם אובייקט File; no-op אלא אם הוא במצב idle
conv.destroy();       // מסיר מאזינים, מרוקן את ה‑mount; המופע אינו ניתן לשימוש לאחר מכן

בניית ממשק משתמש משלך

הווידג'ט הוא רק לקוח עבור חוזה HTTP זה — בנה ממשק משתמש משלך שמתחבר אליו ישירות לחוויית משתמש שונה. שלושת הנתיבים יושבים תחת ענף ASP.NET שבו UseDoconut() מותקן (בדרך כלל /doconut):

נתיבמטרהתגובה מוצלחת
POST ?convert=open (multipart, שדה file)העלאה ופתיחה של מסמך מקור לתצוגה מקדימה200 — { token, pages, sourceExt, allowedTargets }
POST ?token=<token>&convert=run&target=<ext>המרת המקור השמור ל‑target200 — { downloadToken, resultToken, resultPages, downloadName, watermarked }
GET ?convert=download&token=<downloadToken>הזרמת הקובץ המומר200 — בתים של קובץ, Content-Disposition: attachment, Cache-Control: no-store

בתים של המקור שהועלו נשמרים בצד השרת עם TTL של 30 דקות; כאשר חלון זמן זה פג, run משיב 404 והקובץ חייב להיפתח מחדש. התוצאה המומרת נשמרת באותו מאגר — downloadToken מקבל חלון של 30 דקות חדש כאשר ההמרה מסתיימת — בעוד resultToken הוא אסימון סשן צפייה רגיל שהחיים שלו תלויות במטמון הסשן של הצופה, באופן בלתי תלוי במאגר.

sourceExt בתגובה של open אין נקודה מקדימה (למשל "docx") — ההפך מהקונבנצייה של הפרמטר sourceExtension ב‑DocumentConverter.ConvertAsync, הדורש אחת.

מצבי כשל, מקובצים לפי נתיב:

נתיבסטטוסמתיגוף
כל404הווידג'ט אינו מופעל (AddConverterWidget() לא נקרא אף פעם) — נבדק לפני שהנתיב של שלושת הנתיבים נשלחרק סטטוס
כל405verb HTTP שגוי (open/run דורשים POST; download דורש GET)רק סטטוס
open413קובץ שהועלה חורג מ‑MaxUploadMb{ "error": "File is too large." }
open400אין גוף multipart, אין קובץ, או סיומת מקור שלא ניתנת להמרה{ "error": "..." }
run400אסימון פגום (לא GUID), או target שלא ניתן לפרש ל‑ConversionTarget{ "error": "Invalid token." } / { "error": "Unknown target format." }
run400target אינו נמצא ב‑allowedTargets של המקור{ "error": "That target format is not available for this file." }
run404ההעלאה השמורה פגה (TTL של 30 דקות) או שהאסימון מעולם לא נפתח{ "error": "Upload expired — please re-open the file." }
open, run500העיבוד נכשל פנימית{ "error": "<sanitized message>" } — מסונן באותו אופן כמו כל נתיב שגיאה של Doconut; לעולם לא דולף שמות מנוע פנימיים
download400אסימון פגום (לא GUID)רק סטטוס
download404אסימון הורדה לא ידוע או פגוםרק סטטוס

בעלות על משאב

הממיר מחזיר MemoryStream ניתן לחיפוש הממוקם באפס. הקורא הוא הבעלים של הזרם הזה וצריך לשחררו לאחר ההעתקה או החזרת תוכנו. שירות DocumentConverter עצמו הוא חסר מצב ונקבע מהזרקת תלויות; אל תבנה או תשחרר את השירות ידנית.

לגבי הווידג'ט של האינטרנט, מאגרי ההעלאה וההורדה כוללים TTLים של 30 דקות באופן עצמאי. resultToken של הצופה עוקב אחרי משך זמן סשן הצופה במקום זאת. סגירת תוצאת הצופה אינה מוחקת מאגר הורדה שעדיין בתוקף, והגדרת הווידג'ט בדפדפן מחדש אינה מאריכה אף אחד מה‑TTLים.

פתרון בעיות

תסמיןבדיקה
פתרון DocumentConverter נכשלConverterPlugin רישום התרחש בתוך AddDoconut()
היישום נכשל במהלך ההפעלההרישיון הטעון מעניק Converter
המרת הזרם אומרת שהפורמט אינו נתמךsourceExtension כולל את הנקודה המקדימה
קוד JavaScript של הווידג'ט נטען אך הבקשות מחזירות 404AddConverterWidget() לא נקרא
בקשות הווידג'ט משתמשות ב‑URL שגויbasePath תואם את הסניף שבו UseDoconut() ממופה
היעד חסרהשתמש בallowedTargets שהוחזר על ידי convert=open; לא כל מקור תומך בכל יעד של enum
ההורדה פגהחזור על convert=open/convert=run; אסימוני המאגר הם זמניים בכוונה

סימון מים

עם ConverterPlugin רשום, הרישיון של המארח נמצא באחד משלושה מצבים:

מצב הרישיוןשער ההפעלהפלט ההמרה
רישיון צופה בתשלום המעניק Converter, בתקופת התוקף שלועוברנקי — watermarked: false
רישיון הערכה פעיל (הדגמה/NFR)עוברמומר בהצלחה, חותם עם סימן המים של ההערכה — watermarked: true
ללא רישיון, קובץ TRIAL מיושן, או רישיון שאינו זמני שאינו מעניק Converterהיישום לעולם לא מתחיל — שער ההפעלה המתואר למעלה זורק—
רישיון זמני/הדגמה שפגהרישום נשאר לאחר הפקיעהמומר עם סימן המים של ההערכה — watermarked: true

שתי דרכי הקריאה מחשבות את הדגל מאותו כלל: המעטפת C# של DocumentConverter מפיקה אותו פנימית ממצב IsViewerLicensed וIsTemporary של הרישיון, והמתאם ?convert=run של הווידג'ט מבצע את הבדיקה המקבילה (IsViewerLicensed && !IsTrial && !IsTemporary) כדי למלא את השדה watermarked שהוא מחזיר. ניתן לבנות אינטגרציה ולבדוק אותה מקצה לקצה על רישיון הערכה לפני רכישה — רק בתים הפלט משתנים.

האם דף זה היה מועיל?