63 votes

Exporter html en pdf en ASP.NET Core

Je veux exporter un morceau de html vers un fichier pdf mais je n'ai pas de paquet nuget compatible.

Lorsque j'essaie d'installer quelqu'un : "X n'est pas compatible avec netcoreapp1.0 (.NETCoreApp,Version=v1.0)".

Quelqu'un connaît-il un moyen d'exporter vers un pdf en utilisant asp.net core ?

0 votes

23 votes

Net core est différent d'asp.net 5, un autre cadre, d'autres bibliothèques.

3 votes

AspNet Core 1.0 est le nouveau nom. AspNet 5 (anciennement AspNet vNext) était le nom d'origine, mais comme il s'agissait d'un tout nouveau produit, MS a décidé de changer complètement son nom en AspNet Core.

40voto

Jan Blaha Points 115

Vous pouvez utiliser jsreport .net sdk si vous êtes en .net core 2.0 également sans services de nœuds plus complexes. Cela inclut entre autres des filtres pour convertir vos vues razor existantes en pdf. De la docs :

1. Installer les nuggets jsreport.binaire , jsreport.Local y jsreport.AspNetCore

2. En vous Startup.cs le configurer comme suit

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();              
    services.AddJsReport(new LocalReporting()
        .UseBinary(JsReportBinary.GetBinary())
        .AsUtility()
        .Create());
}

3. Ensuite, vous devez ajouter MiddlewareFilter à l'action particulière et spécifiez la conversion que vous voulez utiliser. Dans ce cas, la conversion html en pdf.

[MiddlewareFilter(typeof(JsReportPipeline))]
public IActionResult Invoice()
{
    HttpContext.JsReportFeature().Recipe(Recipe.ChromePdf);
    return View();
}

Vous pouvez accéder à un grand nombre d'autres options pour les en-têtes, les pieds de page ou la mise en page sur le site Web de l'UE. JsReportFeature() . Notez que de la même manière vous pouvez également produire des fichiers excel à partir de html. Voir plus d'informations dans le documentation .

PS : Je suis l'auteur de jsreport.

1 votes

Comment sauvegarder le fichier après cela ?

1 votes

Comment sont gérés les fichiers css et images dans razor ? appels ajax ? la sortie pdf est-elle la même que celle que l'on peut voir dans le navigateur ?

0 votes

Wow - Je n'ai jamais vu cela avant. J'ai jeté un coup d'œil au site et l'application que vous avez mise en place est très complète. Je suis très impressionné par la facilité avec laquelle elle s'intègre à ASP.NET. Avant, j'utilisais toutes sortes de logiciels et je faisais de nombreux cauchemars. Je vous dois beaucoup de bière pour m'avoir fait gagner du temps et avoir facilité la génération de PDF.

23voto

Sjolund Points 411

Copié de ma réponse originale ici Exportation vers le format pdf en utilisant ASP.NET 5 :

Une façon de générer un PDF à partir de HTML dans .NET Core (sans aucune dépendance du cadre .NET) est d'utiliser Node.js dans l'application .NET Core. L'exemple suivant montre comment mettre en œuvre un convertisseur HTML vers PDF dans un projet d'application Web ASP.NET Core propre (modèle d'API Web).

Installer le paquet NuGet Microsoft.AspNetCore.NodeServices

Dans Startup.cs, ajoutez la ligne services.AddNodeServices() comme ceci

public void ConfigureServices(IServiceCollection services)
{
    // ... all your existing configuration is here ...

    // Enable Node Services
    services.AddNodeServices();
}

Maintenant, installez les paquets Node.js requis :

A partir de la ligne de commande, changez le répertoire de travail en Root du projet .NET Core et exécutez ces commandes.

npm init

et suivez les instructions pour créer le fichier package.json

npm install jsreport-core --save
npm install jsreport-jsrender --save
npm install jsreport-phantom-pdf --save

Créer un fichier pdf.js dans la racine du projet contenant

module.exports = function (callback) {
    var jsreport = require('jsreport-core')();

    jsreport.init().then(function () {
        return jsreport.render({
            template: {
                content: '<h1>Hello {{:foo}}</h1>',
                engine: 'jsrender',
                recipe: 'phantom-pdf'
            },
            data: {
                foo: "world"
            }
        }).then(function (resp) {
            callback(/* error */ null, resp.content.toJSON().data);
        });
    }).catch(function (e) {
        callback(/* error */ e, null);
    })
};

Jetez un coup d'œil aquí pour plus d'explications sur jsreport-core .

Maintenant, créez une action dans un contrôleur Mvc qui appelle ce script de Node.js.

[HttpGet]
public async Task<IActionResult> MyAction([FromServices] INodeServices nodeServices)
{
    var result = await nodeServices.InvokeAsync<byte[]>("./pdf");

    HttpContext.Response.ContentType = "application/pdf";

    string filename = @"report.pdf";
    HttpContext.Response.Headers.Add("x-filename", filename);
    HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "x-filename");
    HttpContext.Response.Body.Write(result, 0, result.Length);
    return new ContentResult();
}

Bien sûr, vous pouvez faire tout ce que vous voulez avec les byte[] retourné par nodeServices, dans cet exemple, je l'affiche simplement à partir d'une action de contrôleur afin qu'il puisse être visualisé dans le navigateur.

Vous pouvez aussi échanger les données entre Node.js et .NET Core par une chaîne codée en base64 en utilisant resp.content.toString('base64') en pdf.js et utiliser var result = await nodeServices.InvokeAsync<byte[]>("./pdf"); dans l'action, puis décoder la chaîne encodée en base64.


Alternatives

La plupart des solutions de génération de pdf dépendent toujours du framework .NET 4.5/4.6. Mais il semble y avoir des alternatives payantes disponibles si vous n'aimez pas utiliser Node.js :

  • NReco.PdfGenerator.LT
  • EVO HTML to PDF Converter Client pour .NET Core
  • Winnovative HTML to PDF Converter Client pour .NET Core

Je n'ai essayé aucun de ces produits.

J'espère que nous verrons bientôt des progrès en matière de logiciels libres dans ce domaine.

3 votes

Une idée sur la façon d'utiliser une vue Razor existante (ou toute autre page html) comme entrée ?

0 votes

Si vous cherchez simplement un wkHtmlToPdf-Wrapper, vous pouvez utiliser le mien : github.com/ststeiger/wkHtmlToPdfSharp vous devrez le modifier un peu pour .NET Core.

4 votes

@jao Vous devriez pouvoir modifier pdf.js avec module.exports = function (callback, html) { et dans l'ensemble des modèles content: html puis, dans votre action, faites var result = await nodeServices.InvokeAsync<byte[]>("./pdf", razorRenderedHtmlString); . Cependant, vous aurez probablement du mal avec les css, à moins de les mettre en ligne à l'avance.

6voto

user1646245 Points 89

Vous pouvez vérifier DinkToPdf bibliothèque. Il s'agit d'une enveloppe autour de la bibliothèque wkhtmltopdf pour .NET Core.

Convertisseur synchronisé

Utilisez ce convertisseur dans les applications multifilières et les serveurs web. Les tâches de conversion sont enregistrées dans une collection bloquante et exécutées sur un seul thread.

var converter = new SynchronizedConverter(new PdfTools());

Définir le document à convertir

var doc = new HtmlToPdfDocument()
{
    GlobalSettings = {
        ColorMode = ColorMode.Color,
        Orientation = Orientation.Landscape,
        PaperSize = PaperKind.A4Plus,
    },
    Objects = {
        new ObjectSettings() {
            PagesCount = true,
            HtmlContent = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In consectetur mauris eget ultrices  iaculis. Ut                               odio viverra, molestie lectus nec, venenatis turpis.",
            WebSettings = { DefaultEncoding = "utf-8" },
            HeaderSettings = { FontSize = 9, Right = "Page [page] of [toPage]", Line = true, Spacing = 2.812 }
        }
    }
};

2 votes

Cela fonctionne très bien avec Kestrel, mais pas avec IIS. Une idée de la raison ?

0 votes

Le problème avec IIS est la façon dont il gère les pools d'applications. Pour résoudre ce problème, il faut utiliser les outils Remoting, mais ils ne sont pas pris en charge par .NET Core. Si quelqu'un a une solution pour ce problème, veuillez commenter.

0 votes

Jetez un coup d'œil à [ [github.com/TheSalarKhan/PhantomJs.NetCore]](https://github.com/TheSalarKhan/PhantomJs.NetCore]) il fonctionne sur IIS aussi, et contrairement à wkhtmltopdf il fonctionne sur linux, Mac et Windows.

3voto

Lenny Linus Points 358

J'avais le même problème ! Je voulais générer des fichiers PDF à partir de chaînes HTML. Je suis alors tombé sur PhantomJs qui est un utilitaire en ligne de commande pour convertir des fichiers html en pdf. J'ai écrit un wrapper multiplateforme en C# pour .NET CORE et il fonctionne très bien sous Linux ! Bien que pour l'instant, il ne fonctionne que sur Linux 64 bits, car c'est la seule plateforme que .NET Core supporte actuellement. Le projet peut être trouvé aquí

PhantomJs.NetCore.PdfGenerator gen = new PhantomJs.NetCore.PdfGenerator("/path/to/pantomjsfolder");
string outputFilePath = gen.GeneratePdf("<h1>Hello</h1>","/folder/to/write/file/in");

0 votes

Et les développeurs de macs, Lenny ?

0 votes

Je me suis senti mal quand j'ai vu que tu avais archivé ton dépôt.

2voto

Jean Points 1536

Il s'agit d'une solution fonctionnant pour ASP.NET Core 2.0, qui permet soit de générer des fichiers PDF dynamiques de cshtml vous pouvez les envoyer directement aux utilisateurs et/ou les sauvegarder avant de les envoyer.

Pour compléter Jan Blaha y répond pour plus de flexibilité, vous pouvez utiliser le code suivant :

/// Generate a PDF from a html string
async Task<(string ContentType, MemoryStream GeneratedFileStream)> GeneratePDFAsync(string htmlContent)
{
    IJsReportFeature feature = new JsReportFeature(HttpContext);
    feature.Recipe(Recipe.PhantomPdf);
    if (!feature.Enabled) return (null, null);
    feature.RenderRequest.Template.Content = htmlContent;
    var report = await _RenderService.RenderAsync(feature.RenderRequest);
    var contentType = report.Meta.ContentType;
    MemoryStream ms = new MemoryStream();
    report.Content.CopyTo(ms);
    return (contentType, ms);
}

En utilisant une classe pour rendre les fichiers cshtml sous forme de chaîne, vous pouvez utiliser la fonction service suivant (qui peut être injecté comme un service scopé) :

public class ViewToStringRendererService: ViewExecutor
{
    private ITempDataProvider _tempDataProvider;
    private IServiceProvider _serviceProvider;

    public ViewToStringRendererService(
        IOptions<MvcViewOptions> viewOptions,
        IHttpResponseStreamWriterFactory writerFactory,
        ICompositeViewEngine viewEngine,
        ITempDataDictionaryFactory tempDataFactory,
        DiagnosticSource diagnosticSource,
        IModelMetadataProvider modelMetadataProvider,
        ITempDataProvider tempDataProvider,
        IServiceProvider serviceProvider)
        : base(viewOptions, writerFactory, viewEngine, tempDataFactory, diagnosticSource, modelMetadataProvider)
    {
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;
    }

    public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
    {
        var context = GetActionContext();

        if (context == null) throw new ArgumentNullException(nameof(context));

        var result = new ViewResult()
        {
            ViewData = new ViewDataDictionary<TModel>(
                    metadataProvider: new EmptyModelMetadataProvider(),
                    modelState: new ModelStateDictionary())
            {
                Model = model
            },
            TempData = new TempDataDictionary(
                    context.HttpContext,
                    _tempDataProvider),
            ViewName = viewName,
        };

        var viewEngineResult = FindView(context, result);
        viewEngineResult.EnsureSuccessful(originalLocations: null);

        var view = viewEngineResult.View;

        using (var output = new StringWriter())
        {
            var viewContext = new ViewContext(
                context,
                view,
                new ViewDataDictionary<TModel>(
                    metadataProvider: new EmptyModelMetadataProvider(),
                    modelState: new ModelStateDictionary())
                {
                    Model = model
                },
                new TempDataDictionary(
                    context.HttpContext,
                    _tempDataProvider),
                output,
                new HtmlHelperOptions());

            await view.RenderAsync(viewContext);

            return output.ToString();
        }
    }
    private ActionContext GetActionContext()
    {
        var httpContext = new DefaultHttpContext();
        httpContext.RequestServices = _serviceProvider;
        return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
    }

    /// <summary>
    /// Attempts to find the <see cref="IView"/> associated with <paramref name="viewResult"/>.
    /// </summary>
    /// <param name="actionContext">The <see cref="ActionContext"/> associated with the current request.</param>
    /// <param name="viewResult">The <see cref="ViewResult"/>.</param>
    /// <returns>A <see cref="ViewEngineResult"/>.</returns>
    ViewEngineResult FindView(ActionContext actionContext, ViewResult viewResult)
    {
        if (actionContext == null)
        {
            throw new ArgumentNullException(nameof(actionContext));
        }

        if (viewResult == null)
        {
            throw new ArgumentNullException(nameof(viewResult));
        }

        var viewEngine = viewResult.ViewEngine ?? ViewEngine;

        var viewName = viewResult.ViewName ?? GetActionName(actionContext);

        var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
        var originalResult = result;
        if (!result.Success)
        {
            result = viewEngine.FindView(actionContext, viewName, isMainPage: true);
        }

        if (!result.Success)
        {
            if (originalResult.SearchedLocations.Any())
            {
                if (result.SearchedLocations.Any())
                {
                    // Return a new ViewEngineResult listing all searched locations.
                    var locations = new List<string>(originalResult.SearchedLocations);
                    locations.AddRange(result.SearchedLocations);
                    result = ViewEngineResult.NotFound(viewName, locations);
                }
                else
                {
                    // GetView() searched locations but FindView() did not. Use first ViewEngineResult.
                    result = originalResult;
                }
            }
        }

        if(!result.Success)
            throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", viewName));

        return result;
    }

    private const string ActionNameKey = "action";
    private static string GetActionName(ActionContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (!context.RouteData.Values.TryGetValue(ActionNameKey, out var routeValue))
        {
            return null;
        }

        var actionDescriptor = context.ActionDescriptor;
        string normalizedValue = null;
        if (actionDescriptor.RouteValues.TryGetValue(ActionNameKey, out var value) &&
            !string.IsNullOrEmpty(value))
        {
            normalizedValue = value;
        }

        var stringRouteValue = routeValue?.ToString();
        if (string.Equals(normalizedValue, stringRouteValue, StringComparison.OrdinalIgnoreCase))
        {
            return normalizedValue;
        }

        return stringRouteValue;
    }

}

Puis pour conclure, dans votre contrôleur, en supposant que le modèle de vue cshtml de razor soit /Views/Home/PDFTemplate.cshtml vous pouvez utiliser les éléments suivants.

Note : Le cshtml peut avoir besoin d'être copié lors de la publication (même si les vues sont compilées).

var htmlContent = await _ViewToStringRendererService.RenderViewToStringAsync("Home/PDFTemplate", viewModel);
(var contentType, var generatedFile) = await GeneratePDFAsync(htmlContent);
Response.Headers["Content-Disposition"] = $"attachment; filename=\"{System.Net.WebUtility.UrlEncode(fileName)}\"";

// You may save your file here
using (var fileStream = new FileStream(Path.Combine(folder, fileName), FileMode.Create))
{
   await generatedFile.CopyToAsync(fileStream);
}
// You may need this for re-use of the stream
generatedFile.Seek(0, SeekOrigin.Begin);

return File(generatedFile.ToArray(), "application/pdf", fileName);

0 votes

Comment déclarer le _RenderService ?

0 votes

_RenderService es el ViewToStringRendererService juste en dessous. Il doit être injecté en tant que scoped ou Transient lors du démarrage.

0 votes

@LiquidCore veuillez développer. Outre le ViewToStringRendererService, qui est utile dans plusieurs cas, il s'agit d'un morceau de code de 15 lignes

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X