Экспорт 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;
}