web-dev-qa-db-ja.com

ASP.NET CoreでHTMLをPDFにエクスポート

HtmlをPDFファイルにエクスポートしたいのですが、互換性のあるnugetパッケージはありません。

誰かをインストールしようとすると:「Xはnetcoreapp1.0(.NETCoreApp、Version = v1.0)と互換性がありません。」

Asp.netコアを使用してpdfにエクスポートする方法を知っている人はいますか?

43
Carlos

より複雑なノードサービスなしで.net core 2.0を使用している場合は、 jsreport .net sdk を使用できます。これには、既存のカミソリビューをpdfに変換する他の機能フィルターが含まれます。 docs から:

1. nugetsのインストール jsreport.Binaryjsreport.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.ChromePdf);
    return View();
}

JsReportFeature()で、ヘッダー、フッター、ページレイアウトのその他のオプションにアクセスできます。同じ方法で、htmlからExcelファイルを作成することもできます。詳細は ドキュメント をご覧ください。

PS:私はjsreportの著者です。

23
Jan Blaha

DinkToPdf ライブラリを確認できます。 .NET Coreのwkhtmltopdfライブラリのラッパーです。

同期コンバーター

このコンバーターをマルチスレッドアプリケーションおよびWebサーバーで使用します。変換タスクはブロッキングコレクションに保存され、単一のスレッドで実行されます。

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

私は同じ問題を抱えていました! HTML文字列からPDFファイルを生成したかった。その後、 PhantomJs に出会いました。これは、htmlファイルをpdfに変換するためのコマンドラインユーティリティです。 .NET CORE用のC#でクロスプラットフォームラッパーを作成し、Linuxでうまく動作します!現時点では64ビットLinux専用ですが、これは現在.NET Coreサポートが唯一のプラットフォームであるためです。プロジェクトを見つけることができます こちら

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

これはASP.NET Core 2.0で動作するソリューションで、動的PDFファイルcshtmlから生成し、ユーザーに直接送信および/または保存することができます。送信する前に。

Jan Blaha answer there を補完するために、柔軟性を高めるために、次のコードを使用できます。

/// 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ファイルを文字列としてレンダリングするには、 following service を使用できます(スコープサービスとして挿入できます)。

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