افزونه مبدل

سندها را به ۲۴ فرمت هدف تبدیل کنید

افزونه مبدل 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 می‌دهد، و دلیل آن فقط در لاگ رویدادهای ویندوز (منبع ASP.NET) موجود است. لایسنس‌های موقت Demo/NFR پذیرفته می‌شوند. ببینید تنظیم لایسنس.

تبدیل از C#

هر تبدیل یک MemoryStream قابل جستجو که در موقعیت ۰ قرار دارد، برمی‌گرداند. DocumentConverter بی‌حالت است: آن را از DoconutHost.Services هر جایی که نیاز دارید دریافت کنید و آزادانه دوباره استفاده کنید.

csharp
// DocumentConverter را از DoconutHost.Services حل کنید؛ سازندهٔ آن داخلی است، بنابراین هرگز آن را `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 در overload استریم باید نقطه پیشوندی را شامل شود (".xlsx"، نه "xlsx")، در غیر این صورت حل نمی‌شود. و با وجود نامش، WordToHtmlAsync مقدار Task<Stream> را برمی‌گرداند، نه Task<string> — سند HTML (تصاویر به صورت Base64 جاسازی شده) به شکل یک استریم می‌آید، همانند هر نتیجهٔ دیگر.

در یک کنترلر 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" علامت‌گذاری کنید و تبدیل را به‌عنوان یک کار ناهمزمان صفحه اجرا کنید:

aspx
<%@ Page Language="C#" Async="true" CodeBehind="Convert.aspx.cs" Inherits="MyApp.Convert" %>
csharp
// Web Forms: دستور @Page نیاز به Async="true" دارد و تبدیل به عنوان یک کار ناهمزمان صفحه اجرا می‌شود.
// مسدود کردن آن به جای این‌که (.Result, .Wait(), .GetAwaiter().GetResult()) باعث قفل‌کردن درخواست می‌شود:
// هرگز برنمی‌گردد و هیچ‌چیزی ثبت نمی‌شود.
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، نه HttpContext.Current: پس از await ممکن است مورد دوم 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, web document) مجموعه‌ای از اهداف مجاز خود را دارد. این enum را به‌صورت ثابت در لیست هدف UI خود کد نکنید: ?convert=open مقدار allowedTargets را برای فایل بارگذاری‌شده برمی‌گرداند و این باید انتخاب‌کننده را هدایت کند.

ویجت Drop‑in

نقطه‌های انتهایی ?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 کل بارگذاری را قبل از اینکه Doconut آن را ببیند می‌خواند: maxRequestLength (KB) را نگه دارید و
// maxAllowedContentLength (bytes) در Web.config بالای 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" />           <!-- کیلوبایت: 30 MB -->
</system.web>
<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="31457280" />  <!-- بایت: 30 MB -->
    </requestFiltering>
  </security>
</system.webServer>

سفارشی‌سازی ویجت

گزینه‌های ارسال‌شده به Doconut.convert(selector, options):

گزینهنوعپیش‌فرضیادداشت‌ها
basePathstring/doconutآدرس نقطه انتهایی. '/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 })درخواست باز یا اجرا شکست می‌خوردphase برابر است با 'open' یا 'run'؛ message خطای سرور پاک‌سازی‌شده است

Doconut.convert() نمونهٔ ویجت را برمی‌گرداند: reset() به صفحهٔ رهاسازی باز می‌گردد، loadFile(file) جریان را با یک File آغاز می‌کند، و destroy() آن را حذف می‌کند.

ساخت فرانت‌اند خود

ویجت یک کلاینت برای این قرارداد HTTP در DocImage.axd است:

مسیرهدفپاسخ موفق
POST /DocImage.axd?convert=open (multipart, field 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 نقطهٔ پیش‌ساز (leading dot) ندارد ("docx") — برعکس پارامتر sourceExtension از ConvertAsync.

مسیروضعیتچه‌وقتبدنه
any404ویجت فعال نیست (AddConverterWidget() هرگز فراخوانی نشده است)فقط وضعیت
any405فعل نادرست (open/run نیاز به POST دارند، download نیاز به GET دارد)فقط وضعیت
open413بارگذاری از MaxUploadMb فراتر می‌رود (اما محدودیت‌های IIS نیست){ "error": "File is too large." }
open400بدنهٔ چندبخشی وجود ندارد، فایل نیست، یا نوعی که قابل تبدیل نیست{ "error": "..." }
open403نوع فایل به قابلیت‌ای نیاز دارد که مجوز آن را اعطا نمی‌کند{ "error": "..." }
run400توکن خراب، target ناشناخته، یا هدفی که در allowedTargets نیست{ "error": "..." }
run404بارگذاری منقضی شد یا هرگز باز نشد{ "error": "Upload expired — please re-open the file." }
open, run500پردازش ناموفق بود{ "error": "<sanitized message>" }
download400 / 404توکن دانلود خراب، ناشناخته یا منقضی‌شدهفقط وضعیت

مالکیت منبع

صدا کننده مالک MemoryStream است که یک تبدیل برمی‌گرداند و باید پس از کپی یا بازگرداندن آن، آن را آزاد کند (MVC's File(stream, …) برای شما آن را آزاد می‌کند). ‏DocumentConverter را خودتان ساخت یا آزاد نکنید — سازندهٔ آن داخلی است.

افزودن واترمارک

وضعیت مجوزراه‌اندازیخروجی تبدیل
مجوز نمایشگر پرداخت‌شده که Converter را اعطا می‌کندشروع می‌شودتمیز — watermarked: false
مجوز موقت/دموی فعالشروع می‌شودتبدیل می‌کند، با واترمارک ارزیابی — watermarked: true
بدون مجوز، فایل قدیمی TRIAL یا مجوزی بدون ConverterApplication_Start خطا می‌اندازد؛ هر درخواست پاسخ یک 500 خالی می‌دهد—
مجوز موقت/دموی منقضی‌شدهشروع می‌شودتبدیل می‌کند، با واترمارک ارزیابی — watermarked: true

یک ادغام می‌تواند به‌صورت انتها به انتها بر روی یک مجوز ارزیابی ساخته و تست شود قبل از خرید — فقط بایت‌های خروجی تغییر می‌کنند.

عیب‌یابی

علامتبررسی
هر درخواست پس از افزودن افزونه، یک 500 خالی پاسخ می‌دهدمجوز Converter را اعطا می‌کند — لاگ رویداد ویندوز، منبع 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 را تکرار کنید؛ توکن‌ها عمداً موقت هستند

آیا این صفحه مفید بود؟