Экспорт html в pdf в ASP.NET Core

Я хочу экспортировать кусок HTML в PDF файл, но у меня нет совместимого пакета Nuget.

Когда я пытаюсь установить кого-либо: "X не совместим с netcoreapp1.0 (.NETCoreApp, Version = v1.0)".

Кто-нибудь знает какой-либо способ экспорта в PDF с использованием ядра asp.net??

Ответы

Ответ 1

Скопировано из моего первоначального ответа здесь. Экспорт в pdf с помощью ASP.NET 5:

Один из способов генерации pdf файла из html в.NET Core (без каких-либо зависимостей.NET framework) - использует Node.js из приложения.NET Core. В следующем примере показано, как реализовать конвертер HTML в PDF в чистый проект ASP.NET Core Web Application (шаблон веб-API).

Установите пакет NuGet Microsoft.AspNetCore.NodeServices

В Startup.cs добавьте строку services.AddNodeServices() как это

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

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

Теперь установите необходимые пакеты Node.js:

Из командной строки измените рабочий каталог на корень проекта.NET Core и запустите эти команды.

npm init

и следуйте инструкциям для создания файла package.json

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

Создайте файл pdf.js в корне проекта, содержащего

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

Посмотрите здесь, чтобы jsreport-core больше о jsreport-core.

Теперь создайте действие в контроллере Mvc, который вызывает этот сценарий 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();
}

Конечно, вы можете делать все, что хотите, с byte[] возвращаемым из nodeServices, в этом примере я просто выводю его из действия контроллера, чтобы его можно было просмотреть в браузере.

Вы также можете обмениваться данными между Node.js и.NET Core с помощью кодированной base64 строки с использованием resp.content.toString('base64') в pdf.js и использовать var result = await nodeServices.InvokeAsync<byte[]>("./pdf"); в действии, а затем декодировать строку с кодировкой base64.


альтернативы

Большинство решений для создания PDF файлов по-прежнему зависят от платформы.NET 4.5/4.6. Но, похоже, есть некоторые платные альтернативы, если вам не нравится использовать Node.js:

  • NReco.PdfGenerator.LT
  • Конвертер EVO HTML в PDF для.NET Core
  • Винновативный конвертер HTML в PDF для.NET Core

Однако я не пробовал ни одного из них.

Надеюсь, в скором времени мы увидим прогресс с открытым исходным кодом в этой области.

Ответ 2

Вы можете использовать jsreport.net sdk, если вы находитесь в .net core 2.0 также без дополнительных node сервисов. Это включает в себя среди других функций фильтры для преобразования существующих видов бритвы в pdf. Из docs:

1. Установите nugets jsreport.Binary, jsreport.Local и jsreport.AspNetCore

2. У вас Startup.cs настройте его как

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

3. Затем вам нужно добавить атрибут MiddlewareFilter к конкретному действию и указать, какое преобразование вы хотите использовать. В этом случае преобразование html в pdf.

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

Вы можете получить множество других параметров для заголовков, нижних колонтитулов или макета страницы на JsReportFeature(). Обратите внимание, что вы также можете создавать файлы excel из html. Подробнее см. В документации .

PS: Я автор jsreport.

Ответ 3

Вы можете проверить DinkToPdf библиотеку. Это оболочка библиотеки wkhtmltopdf для .NET Core.

Синхронизированный конвертер

Используйте этот конвертер в многопоточных приложениях и веб-серверах. Задачи преобразования сохраняются для блокировки коллекции и выполняются в одном потоке.

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

Определить документ для преобразования

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

Ответ 4

У меня была такая же проблема! Я хотел создать файлы PDF из строк HTML. Затем я наткнулся на PhantomJs, который является утилитой командной строки для преобразования html файлов в pdf. Я написал кросс-платформенную оболочку над ним в С# для .NET CORE и отлично работает в Linux! Хотя на данный момент это единственное для 64-битного Linux, потому что это единственная платформа .NET Core Support в настоящее время. Проект можно найти здесь

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

Ответ 5

Лучшая структура, которую я нашел на сегодняшний день, которая требует минимального объема работы, https://github.com/aaxelm/Rotativa.NetCore

Ответ 6

Это решение, работающее для ASP.NET Core 2.0, которое позволяет либо генерировать динамические PDF файлы из cshtml, напрямую отправлять их пользователям и/или сохранять их перед отправкой.

Чтобы дополнить Jan Blaha ответ там, для большей гибкости, вы можете использовать следующий код:

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

Используя класс для рендеринга файлов cshtml как строку, вы можете использовать следующую услугу (которая может быть введена как служба с областью действия):

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

}

Затем, чтобы заключить, что в вашем контроллере, предположив, что шаблон просмотра бритвы cshtml является /Views/Home/PDFTemplate.cshtml вы можете использовать следующее.

Примечание. Файл cshtml может быть скопирован при публикации (даже если скомпилированные представления).

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

Ответ 7

На всякий случай, если кто-нибудь использует .NET CORE> 2.0 (ТОЛЬКО для WINDOWS)

Это бесплатная и простая в использовании библиотека: https://code.msdn.microsoft.com/Convert-from-HTML-to-PDF-d63582e8

public IActionResult OnPost() 
{ 
    // instantiate a html to pdf converter object 
    HtmlToPdf converter = new HtmlToPdf(); 

    // create a new pdf document converting an url 
    PdfDocument doc = converter.ConvertUrl(TxtUrl); 

    // save pdf document 
    byte[] pdf = doc.Save(); 

    // close pdf document 
    doc.Close(); 

    // return resulted pdf document 
    FileResult fileResult = new FileContentResult(pdf, "application/pdf"); 
    fileResult.FileDownloadName = "Document.pdf"; 
    return fileResult; 
}