תוסף הממיר

המרת מסמכים ל‑24 פורמטים שונים

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

התקנת החבילה

powershell
Install-Package Doconut.NETFramework.Converter

שמור על חבילת הממיר באותה גרסה כמו Doconut.NETFramework.

רישום התוסף

כל תוסף נרשם באותו אופן: AddPlugin<TPlugin>() ב‑DoconutHost.Initialize.

csharp
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 בכל מקום שבו אתה צריך אותו והשתמש בו מחדש בחופשיות.

csharp
// Resolve DocumentConverter from DoconutHost.Services; its constructor is internal, so never `new` it.
Stream pdf = await converter.ConvertAsync("contract.docx", ConversionTarget.Pdf, ct: ct);
csharp
// 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);
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, כמו כל תוצאה אחרת.

במבקר MVC 5

הפוך את הפעולה לאסינכרונית ו‑await את ההמרה:

csharp
[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 אסינכרוני של הדף:

aspx
<%@ Page Language="C#" Async="true" CodeBehind="Convert.aspx.cs" Inherits="MyApp.Convert" %>
csharp
// 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 שם.

פורמטים יעד

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

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

ווידג'ט משולב

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

csharp
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.
html
<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 מעל למגבלת הווידג'ט:

xml
<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):

אפשרותסוגברירת מחדלהערות
basePathstring/doconutכתובת URL של נקודת הקצה. הגדר '/DocImage.axd' בחבילה זו
resPathstring/doconut-resהתקבל לצורך עקביות עם ווידג'טים אחרים של Doconut; ווידג'ט הממיר אינו בונה שום URL ממנו
maxUploadMbnumber25בדיקה מקדימה בצד הלקוח בלבד; השרת אוכף את המגבלה שלו
licenseUrlstring | nullnullהופך את הודעת סימן המים במסך התוצאה לקישור
labelsobject{}מחליף כל תת‑קבוצה של המחרוזות האנגליות של הווידג'ט

פונקציות קריאה חוזרת:

פונקציית קריאה חוזרתמתרחש כאשרנתונים
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>המרת המקור שהועלה ל-target200 — { 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)רק סטטוס
open413ההעלאה חורגת מ-MaxUploadMb (אך לא מגבלות IIS){ "error": "File is too large." }
open400אין גוף multipart, אין קובץ, או סוג שלא ניתן להמיר{ "error": "..." }
open403סוג הקובץ דורש יכולת שהרישיון אינו מעניק{ "error": "..." }
run400אסימון פגום, target לא ידוע, או יעד שלא נמצא ב-allowedTargets{ "error": "..." }
run404ההעלאה פגה או שמעולם לא נפתחה{ "error": "Upload expired — please re-open the file." }
open, run500ההעיבוד נכשל{ "error": "<sanitized message>" }
download400 / 404אסימון הורדה פגום, לא ידוע, או פג תוקףרק סטטוס

בעלות על המשאב

המתקשר הוא הבעלים של ה-MemoryStream שההמרה מחזירה ויש לפנות אותו לאחר ההעתקה או החזרה (המתודה File(stream, …) של MVC מפנה אותו עבורך). אל תבנה או תפרק את DocumentConverter בעצמך — הבנאי שלו פנימי.

סימון מים

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

ניתן לבנות ולבדוק אינטגרציה מקצה לקצה ברישיון הערכה לפני הרכישה — רק בתים הפלט משתנים.

פתרון בעיות

תסמיןבדיקה
כל בקשה מחזירה 500 ריק לאחר הוספת הפלאגיןהרישיון מעניק Converter — יומן אירועי Windows, מקור ASP.NET
פתירת DocumentConverter גורמת לשגיאהAddPlugin<ConverterPlugin>() נמצא ב-DoconutHost.Initialize
המרת Web Forms לעולם לא מחזירההדף נתקע ב-ConvertAsync — השתמש ב-Async="true" וב-RegisterAsyncTask
המרת הזרם מודיעה שהפורמט אינו נתמךsourceExtension כולל את הנקודה המקדימה
הווידג'ט נטען אך הבקשות שלו מחזירות 404AddConverterWidget() לא נקרא, או ש-basePath אינו '/DocImage.axd'
העלאה גדולה מציגה דף שגיאת IIS במקום הודעת הווידג'טmaxRequestLength / maxAllowedContentLength מתחת ל-MaxUploadMb
יעד חסרהשתמש ב-allowedTargets מ-convert=open
ההורדה פגהחזור על convert=open / convert=run; האסימונים זמניים בכוונה

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