תוסף הממיר
המרת מסמכים ל‑24 פורמטים שונים
תוסף הממיר הופך את Doconut לשירות המרת מסמכים. הוא תורם את המנוע שמאחורי המעטפת הציבורית DocumentConverter ו‑— במצב אופציונלי — וידג׳ט נשלף שמוגש על‑ידי DocImage.axd, כך שאתה יכול להמיר מסמכים מ‑C#, מהוידג׳ט, או מממשק משתמש שאתה בונה בעצמך.
התקנת החבילה
Install-Package Doconut.NETFramework.Converterשמור על חבילת הממיר באותה גרסה כמו Doconut.NETFramework.
רישום התוסף
כל תוסף נרשם באותו אופן: AddPlugin<TPlugin>() ב‑DoconutHost.Initialize.
Doconut.DoconutHost.Initialize(options =>
{
options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
});אם אין רישיון פעיל שמעניק את היכולת
Converter— אין רישיון, קובץTRIALישן, או רישיון ללא היכולת —DoconutHost.Initializeזורק משגרApplication_Start. ב‑System.Web המשמעות היא שכל בקשה מחזירה500ריק, והסיבה נמצאת רק ביומן האירועים של Windows (מקור ASP.NET). רישיונות Demo/NFR זמניים מתקבלים. ראה הגדרת רישיון.
המרה מ‑C#
כל המרה מחזירה MemoryStream שניתן לחפש בו, ממוקם ב‑0. DocumentConverter הוא חסר מצב: קבל אותו מ‑DoconutHost.Services בכל מקום שבו אתה צריך אותו והשתמש בו מחדש בחופשיות.
// Resolve DocumentConverter from DoconutHost.Services; its constructor is internal, so never `new` it.
Stream pdf = await converter.ConvertAsync("contract.docx", ConversionTarget.Pdf, ct: ct);// sourceExtension includes the leading dot. password is null unless the document is protected.
Stream png = await converter.ConvertAsync(upload, ".xlsx", ConversionTarget.Png, password: null, ct: ct);Stream html = await converter.WordToHtmlAsync("report.docx", ct);Stream docx = await converter.HtmlToWordAsync(html, ConversionTarget.Docx, ct);שתי פרטים שקל לטעות בהם: sourceExtension בגרסת ה‑stream חייב לכלול את הנקודה המקדימה (".xlsx", ולא "xlsx"), אחרת הוא לא ייפתר. ולמרות שמו, WordToHtmlAsync מחזיר Task<Stream>, ולא Task<string> — מסמך ה‑HTML (תמונות משובצות כ‑Base64) מגיע כ‑stream, כמו כל תוצאה אחרת.
במבקר MVC 5
הפוך את הפעולה לאסינכרונית ו‑await את ההמרה:
[HttpPost]
public async Task<ActionResult> ToPdf(HttpPostedFileBase file)
{
var converter = Doconut.DoconutHost.Services.GetRequiredService<DocumentConverter>();
Stream pdf = await converter.ConvertAsync(
file.InputStream, Path.GetExtension(file.FileName), ConversionTarget.Pdf,
password: null, ct: Response.ClientDisconnectedToken);
return File(pdf, "application/pdf", Path.GetFileNameWithoutExtension(file.FileName) + ".pdf");
}ב‑Web Forms: אל תחסום לעולם על המרה
דף Web Forms פועל בהקשר בקשה חד‑חוטי. ConvertAsync ממשיך באותו הקשר כאשר הוא מסתיים, ולכן דף שממתין לו בצורה סינכרונית — .Result, .Wait(), .GetAwaiter().GetResult() — מחזיק את החוט שההמרה זקוקה לו להשלמה. הבקשה לעולם לא חוזרת. הדפדפן ממשיך להסתובב, IIS לא רושם דבר, ושאר האתר ממשיך לעבוד, ולכן זה נראה כהמרה איטית ולא כנעילה.
סמן את הדף Async="true" והרץ את ההמרה כ‑task אסינכרוני של הדף:
<%@ Page Language="C#" Async="true" CodeBehind="Convert.aspx.cs" Inherits="MyApp.Convert" %>// Web Forms: the @Page directive needs Async="true", and the conversion runs as a page async task.
// Blocking on it instead (.Result, .Wait(), .GetAwaiter().GetResult()) deadlocks the request:
// it never returns, and nothing is logged.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) return;
RegisterAsyncTask(new PageAsyncTask(async ct =>
{
var converter = Doconut.DoconutHost.Services.GetRequiredService<DocumentConverter>();
using (var pdf = await converter.ConvertAsync(
Server.MapPath("~/App_Data/contract.docx"), ConversionTarget.Pdf, ct: ct))
using (var ms = new MemoryStream())
{
if (pdf.CanSeek) pdf.Position = 0;
await pdf.CopyToAsync(ms);
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=contract.pdf");
Response.BinaryWrite(ms.ToArray());
// Context, not HttpContext.Current: after an await the latter can be null.
Context.ApplicationInstance.CompleteRequest();
}
}));
}בלי Async="true" בהוראת @Page, RegisterAsyncTask מתעלם בשקט. אחרי await, השתמש ב‑Context של הדף; HttpContext.Current יכול להיות null שם.
פורמטים יעד
Pdf, Docx, Doc, Html, Xlsx, Pptx, Png, Jpeg, Csv, Tiff, Bmp, Gif, Svg, Xml,
Txt, Xls, Jp2, Rtf, Odt, Ods, Odp, Epub, Xps, Webpלא כל מקור מתמיר לכל יעד: לכל משפחת פורמט של מקור (Word, Excel, PowerPoint, PDF, CAD, Image, Email, Diagram, Project, PSD, מסמך אינטרנט) יש סט משלה של יעדים מותרים. אל תקודד קבוע את המונה הזה ברשימת היעדים של הממשק שלך: ?convert=open מחזיר את allowedTargets עבור הקובץ שהועלה, וזה מה שצריך להניע את בחירת היעד.
ווידג'ט משולב
הקצוות ?convert=open|run|download של הווידג'ט מסופקים על ידי DocImage.axd והם כבויים כברירת מחדל. אפשר אותם ליד רישום הפלאגין:
Doconut.DoconutHost.Initialize(options =>
{
options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
options.AddConverterWidget(widget =>
{
widget.MaxUploadMb = 25;
});
});
// System.Web reads the whole upload before Doconut sees it: keep maxRequestLength (KB) and
// maxAllowedContentLength (bytes) in Web.config above the widget's MaxUploadMb.<div id="doconut-convert"></div>
<script src="/doconut-res/js/doconutConverter.js"></script>
<script>
Doconut.convert('#doconut-convert', { basePath: '/DocImage.axd', maxUploadMb: 25 });
</script>שתי דברים לבדוק כאשר הווידג'ט מתנהג בצורה לא נכונה:
basePath: '/DocImage.axd'. ברירת המחדל של הווידג'ט,/doconut, היא נקודת הקצה של ASP.NET Core. בחבילה זו, נקודות הקצה הן המטפלDocImage.axdמ‑Web.config.- הסקריפט ונקודות הקצה עצמאיים. מודול המשאבים מגיש
doconutConverter.jsבין אם הווידג'ט מופעל ובין אם לא. ללאAddConverterWidget(), הווידג'ט נטען, מצייר את אזור ההפלה ונראה תקין לחלוטין — והפעולה הראשונה שלו מחזירה404.
מגבלות העלאה
ASP.NET קורא את כל גוף הבקשה לפני שה‑DocImage.axd פועל. העלאה שמעל MaxUploadMb אך מתחת למגבלות המארח מחזירה את ה‑JSON 413 של הווידג'ט ("File is too large."). העלאה שמעל מגבלות המארח נדחית תחילה על ידי IIS, עם דף שגיאה שהווידג'ט אינו יכול לקרוא. שמור את שני המגבלות במקובץ Web.config מעל למגבלת הווידג'ט:
<system.web>
<httpRuntime maxRequestLength="30720" /> <!-- KILOBYTES: 30 MB -->
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="31457280" /> <!-- BYTES: 30 MB -->
</requestFiltering>
</security>
</system.webServer>התאמת הווידג'ט
אפשרויות שמועברות ל‑Doconut.convert(selector, options):
| אפשרות | סוג | ברירת מחדל | הערות |
|---|---|---|---|
basePath | string | /doconut | כתובת URL של נקודת הקצה. הגדר '/DocImage.axd' בחבילה זו |
resPath | string | /doconut-res | התקבל לצורך עקביות עם ווידג'טים אחרים של Doconut; ווידג'ט הממיר אינו בונה שום URL ממנו |
maxUploadMb | number | 25 | בדיקה מקדימה בצד הלקוח בלבד; השרת אוכף את המגבלה שלו |
licenseUrl | string | null | null | הופך את הודעת סימן המים במסך התוצאה לקישור |
labels | object | {} | מחליף כל תת‑קבוצה של המחרוזות האנגליות של הווידג'ט |
פונקציות קריאה חוזרת:
| פונקציית קריאה חוזרת | מתרחש כאשר | נתונים |
|---|---|---|
onReady() | הווידג'ט רינדד את מסך ההפלה שלו | — |
onSourceLoaded({ token, pages, sourceExt, allowedTargets }) | ?convert=open מצליח | טוקן המקור, מספר העמודים, סיומת המקור (ללא נקודה מובילה), יעדים מותרים |
onConverted({ downloadToken, resultToken, resultPages, downloadName, watermarked, target }) | ?convert=run מצליח | תשובת הריצה בנוסף ל‑target המבוקש |
onDownload({ downloadName, downloadToken }) | המשתמש לוחץ על הורדה | מתרחש יחד עם ההורדה של הדפדפן עצמו |
onError({ phase, message }) | בקשת open או run נכשלת | phase הוא 'open' או 'run'; message הוא השגיאה המסוננת מהשרת |
Doconut.convert() מחזיר את מופע הווידג'ט: reset() מחזיר למסך ההפלה, loadFile(file) מתחיל את הזרימה עם File, ו‑destroy() מסיר אותו.
בנה את החזית שלך
הווידג'ט הוא לקוח לחוזה HTTP זה ב-DocImage.axd:
| נתיב | מטרה | תשובת הצלחה |
|---|---|---|
POST /DocImage.axd?convert=open (multipart, שדה file) | העלאה ופתיחה של מסמך מקור | 200 — { token, pages, sourceExt, allowedTargets } |
POST /DocImage.axd?token=<token>&convert=run&target=<ext> | המרת המקור שהועלה ל-target | 200 — { downloadToken, resultToken, resultPages, downloadName, watermarked } |
GET /DocImage.axd?convert=download&token=<downloadToken> | הזרמת הקובץ המומר | 200 — הקובץ, Content-Disposition: attachment |
מקורות שהועלו נשמרים למשך 30 דקות; לאחר מכן, run מחזיר 404 והקובץ חייב להיפתח שוב. downloadToken מקבל חלון של 30 דקות משלו כאשר ההמרה מסתיימת. resultToken הוא אסימון צופה רגיל העוקב אחרי משך חיי סשן הצופה.
sourceExt בתשובת open אינו כולל נקודה מקדימה ("docx" ) — ההפך מהפרמטר sourceExtension של ConvertAsync.
| נתיב | סטטוס | מתי | גוף |
|---|---|---|---|
| כל | 404 | הווידג'ט אינו מופעל (AddConverterWidget() מעולם לא נקרא) | רק סטטוס |
| כל | 405 | פועל שגוי (open/run דורשים POST, download דורש GET) | רק סטטוס |
open | 413 | ההעלאה חורגת מ-MaxUploadMb (אך לא מגבלות IIS) | { "error": "File is too large." } |
open | 400 | אין גוף multipart, אין קובץ, או סוג שלא ניתן להמיר | { "error": "..." } |
open | 403 | סוג הקובץ דורש יכולת שהרישיון אינו מעניק | { "error": "..." } |
run | 400 | אסימון פגום, target לא ידוע, או יעד שלא נמצא ב-allowedTargets | { "error": "..." } |
run | 404 | ההעלאה פגה או שמעולם לא נפתחה | { "error": "Upload expired — please re-open the file." } |
open, run | 500 | ההעיבוד נכשל | { "error": "<sanitized message>" } |
download | 400 / 404 | אסימון הורדה פגום, לא ידוע, או פג תוקף | רק סטטוס |
בעלות על המשאב
המתקשר הוא הבעלים של ה-MemoryStream שההמרה מחזירה ויש לפנות אותו לאחר ההעתקה או החזרה (המתודה File(stream, …) של MVC מפנה אותו עבורך). אל תבנה או תפרק את DocumentConverter בעצמך — הבנאי שלו פנימי.
סימון מים
| מצב רישיון | הפעלה | פלט המרה |
|---|---|---|
רישיון צופה בתשלום המעניק Converter | מתחיל | נקי — watermarked: false |
| רישיון זמני/דמו פעיל | מתחיל | ממיר, עם סימן המים של ההערכה — watermarked: true |
אין רישיון, קובץ TRIAL מדור קודם, או רישיון ללא Converter | Application_Start זורק שגיאה; כל בקשה מחזירה 500 ריק | — |
| רישיון זמני/דמו שפג | מתחיל | ממיר, עם סימן המים של ההערכה — watermarked: true |
ניתן לבנות ולבדוק אינטגרציה מקצה לקצה ברישיון הערכה לפני הרכישה — רק בתים הפלט משתנים.
פתרון בעיות
| תסמין | בדיקה |
|---|---|
כל בקשה מחזירה 500 ריק לאחר הוספת הפלאגין | הרישיון מעניק Converter — יומן אירועי Windows, מקור ASP.NET |
פתירת DocumentConverter גורמת לשגיאה | AddPlugin<ConverterPlugin>() נמצא ב-DoconutHost.Initialize |
| המרת Web Forms לעולם לא מחזירה | הדף נתקע ב-ConvertAsync — השתמש ב-Async="true" וב-RegisterAsyncTask |
| המרת הזרם מודיעה שהפורמט אינו נתמך | sourceExtension כולל את הנקודה המקדימה |
הווידג'ט נטען אך הבקשות שלו מחזירות 404 | AddConverterWidget() לא נקרא, או ש-basePath אינו '/DocImage.axd' |
| העלאה גדולה מציגה דף שגיאת IIS במקום הודעת הווידג'ט | maxRequestLength / maxAllowedContentLength מתחת ל-MaxUploadMb |
| יעד חסר | השתמש ב-allowedTargets מ-convert=open |
| ההורדה פגה | חזור על convert=open / convert=run; האסימונים זמניים בכוונה |
האם דף זה היה מועיל?