Plugin Convertidor

Convertir documentos a 24 formatos de destino

El plugin Converter convierte a Doconut en un servicio de conversión de documentos. Contribuye con el motor detrás de la fachada pública DocumentConverter y — opcionalmente — un widget listo para usar servido por DocImage.axd, de modo que pueda convertir documentos desde C#, desde el widget, o desde una interfaz frontal que usted mismo escriba.

Instalar el paquete

powershell
Install-Package Doconut.NETFramework.Converter

Mantenga el paquete Converter en la misma versión que Doconut.NETFramework.

Registrar el plugin

Cada plugin se registra de la misma manera: AddPlugin<TPlugin>() en DoconutHost.Initialize.

csharp
Doconut.DoconutHost.Initialize(options =>
{
    options.LicensePath = HostingEnvironment.MapPath("~/wwwroot/Doconut.Viewer.lic");
    options.AddPlugin<Doconut.Plugins.Converter.ConverterPlugin>();
});

Si ninguna licencia activa otorga la capacidad Converter — sin licencia, un archivo legado TRIAL, o una licencia que no la incluya — DoconutHost.Initialize lanza una excepción desde Application_Start. En System.Web eso significa que cada solicitud responde con un 500 vacío, y la razón solo aparece en el registro de eventos de Windows (fuente ASP.NET). Se aceptan licencias temporales Demo/NFR. Consulte Configuración de Licencia.

Convertir desde C#

Cada conversión devuelve un MemoryStream buscable posicionado en 0. DocumentConverter es sin estado: resuélvalo desde DoconutHost.Services donde lo necesite y reutilícelo libremente.

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);

Dos detalles que es fácil equivocarse: sourceExtension en la sobrecarga de stream debe incluir el punto inicial (".xlsx", no "xlsx"), de lo contrario no se resuelve. Y a pesar de su nombre, WordToHtmlAsync devuelve Task<Stream>, no Task<string> — el documento HTML (imágenes incrustadas como Base64) llega como un stream, como cualquier otro resultado.

En un controlador MVC 5

Haga que la acción sea asíncrona y await la conversión:

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");
}

En Web Forms: nunca bloquee una conversión

Una página de Web Forms se ejecuta en un contexto de solicitud de un solo hilo. ConvertAsync se reanuda en ese contexto cuando finaliza, por lo que una página que lo espera de forma síncrona — .Result, .Wait(), .GetAwaiter().GetResult() — retiene el propio hilo que la conversión necesita para terminar. La solicitud nunca regresa. El navegador sigue girando, IIS no registra nada, y el resto del sitio sigue funcionando, por lo que parece una conversión lenta en lugar de un deadlock.

Marque la página Async="true" y ejecute la conversión como una tarea asíncrona de la página:

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();
        }
    }));
}

Sin Async="true" en la directiva @Page, RegisterAsyncTask se ignora silenciosamente. Después de un await, use el Context de la página; HttpContext.Current puede ser null allí.

Formatos de destino

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

No todas las fuentes se convierten a todos los destinos: la familia de formatos de cada fuente (Word, Excel, PowerPoint, PDF, CAD, Image, Email, Diagram, Project, PSD, web document) tiene su propio conjunto de destinos permitidos. No codifiques este enum como la lista de destinos de tu UI: ?convert=open devuelve los allowedTargets para el archivo subido, y eso es lo que debe impulsar un selector.

Widget incrustable

Los endpoints ?convert=open|run|download del widget son servidos por DocImage.axd y están desactivados por defecto. Habilítalos junto al registro del plugin:

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>

Dos cosas a comprobar cuando el widget se comporta de forma errónea:

  • basePath: '/DocImage.axd'. El valor predeterminado del widget, /doconut, es el endpoint de ASP.NET Core. En este paquete, los endpoints son el controlador DocImage.axd de Web.config.
  • El script y los endpoints son independientes. El módulo de recursos sirve doconutConverter.js tanto si el widget está habilitado como si no. Sin AddConverterWidget(), el widget se carga, pinta su zona de arrastre y parece completamente correcto — y su primera acción responde 404.

Límites de carga

ASP.NET lee todo el cuerpo de la solicitud antes de que se ejecute DocImage.axd. Una carga superior a MaxUploadMb pero por debajo de los límites del host devuelve el propio JSON 413 del widget ("File is too large."). Una carga superior a los límites del host es rechazada primero por IIS, con una página de error que el widget no puede leer. Mantén ambos límites del host en Web.config por encima del límite del widget:

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>

Personalizar el widget

Opciones pasadas a Doconut.convert(selector, options):

OpciónTipoPredeterminadoNotas
basePathstring/doconutLa URL del endpoint. Establece '/DocImage.axd' en este paquete
resPathstring/doconut-resAceptado por consistencia con otros widgets Doconut; el widget de conversión no construye ninguna URL a partir de él
maxUploadMbnumber25Solo verificación previa del lado del cliente; el servidor impone su propio límite
licenseUrlstring | nullnullConvierte el aviso de marca de agua en la pantalla de resultados en un enlace
labelsobject{}Sobrescribe cualquier subconjunto de las cadenas en inglés del widget

Funciones de devolución de llamada:

FunciónSe dispara cuandoCarga útil
onReady()El widget ha renderizado su pantalla de arrastre—
onSourceLoaded({ token, pages, sourceExt, allowedTargets })?convert=open tiene éxitoToken de la fuente, recuento de páginas, extensión de la fuente (sin punto inicial), destinos permitidos
onConverted({ downloadToken, resultToken, resultPages, downloadName, watermarked, target })?convert=run tiene éxitoLa respuesta de ejecución más el target solicitado
onDownload({ downloadName, downloadToken })El usuario hace clic en DescargarSe dispara junto con la propia descarga del navegador
onError({ phase, message })Una solicitud open o run fallaphase es 'open' o 'run'; message es el error del servidor sanitizado

Doconut.convert() devuelve la instancia del widget: reset() vuelve a la pantalla de arrastre, loadFile(file) inicia el flujo con un File, y destroy() lo elimina.

Construye tu propio frontend

El widget es un cliente de este contrato HTTP en DocImage.axd:

RutaPropósitoRespuesta exitosa
POST /DocImage.axd?convert=open (multipart, field file)Subir y abrir un documento fuente200 — { token, pages, sourceExt, allowedTargets }
POST /DocImage.axd?token=<token>&convert=run&target=<ext>Convertir el origen subido a target200 — { downloadToken, resultToken, resultPages, downloadName, watermarked }
GET /DocImage.axd?convert=download&token=<downloadToken>Transmitir el archivo convertido200 — el archivo, Content-Disposition: attachment

Los orígenes subidos se conservan durante 30 minutos; después de ese tiempo, run responde 404 y el archivo debe abrirse de nuevo. downloadToken dispone de su propia ventana de 30 minutos cuando la conversión finaliza. resultToken es un token de visor ordinario que sigue la vida útil de la sesión del visor.

sourceExt en la respuesta de open no lleva punto inicial ("docx"), a diferencia del parámetro sourceExtension de ConvertAsync.

RutaEstadoCuándoCuerpo
cualquiera404El widget no está habilitado (AddConverterWidget() nunca se llamó)solo estado
cualquiera405Verbo incorrecto (open/run requieren POST, download requiere GET)solo estado
open413La carga supera MaxUploadMb (pero no los límites de IIS){ "error": "File is too large." }
open400No hay cuerpo multipart, no hay archivo, o un tipo que no se puede convertir{ "error": "..." }
open403El tipo de archivo necesita una capacidad que la licencia no otorga{ "error": "..." }
run400Un token mal formado, un target desconocido, o un objetivo que no está en allowedTargets{ "error": "..." }
run404La carga expiró o nunca se abrió{ "error": "Upload expired — please re-open the file." }
open, run500Falló el procesamiento{ "error": "<sanitized message>" }
download400 / 404Un token de descarga mal formado, desconocido o expiradosolo estado

Propiedad de recursos

El llamador posee el MemoryStream que devuelve una conversión y debe disponer de él después de copiarlo o devolverlo (el File(stream, …) de MVC lo dispone por ti). No construyas ni dispongas DocumentConverter tú mismo — su constructor es interno.

Marca de agua

Estado de la licenciaInicioSalida de la conversión
Licencia de visor pagada que otorga ConverterIniciaLimpia — watermarked: false
Licencia Temporal/Demo activaIniciaConvierte, con la marca de agua de evaluación — watermarked: true
Sin licencia, un archivo heredado TRIAL, o una licencia sin ConverterApplication_Start lanza excepción; cada solicitud responde un 500 vacío—
Licencia Temporal/Demo expiradaIniciaConvierte, con la marca de agua de evaluación — watermarked: true

Una integración puede construirse y probarse de extremo a extremo con una licencia de evaluación antes de la compra — solo cambian los bytes de salida.

Solución de problemas

SíntomaVerificación
Cada solicitud responde un 500 vacío después de añadir el pluginLa licencia otorga Converter — registro de eventos de Windows, origen ASP.NET
Resolver DocumentConverter lanza excepciónAddPlugin<ConverterPlugin>() está en DoconutHost.Initialize
Una conversión Web Forms nunca devuelveLa página se bloquea en ConvertAsync — usa Async="true" y RegisterAsyncTask
La conversión de flujo indica que el formato no es compatiblesourceExtension incluye el punto inicial
El widget se carga pero sus solicitudes devuelven 404No se llamó a AddConverterWidget(), o basePath no es '/DocImage.axd'
Una carga grande muestra una página de error de IIS en lugar del mensaje del widgetmaxRequestLength / maxAllowedContentLength están por debajo de MaxUploadMb
Falta un objetivoUsa los allowedTargets de convert=open
La descarga expiróRepite convert=open / convert=run; los tokens son temporales a propósito

¿Fue útil esta página?