This commit is contained in:
code@server 2026-04-05 12:22:56 +08:00
parent 578d84cb08
commit a919e23494
5 changed files with 109 additions and 29 deletions

View File

@ -8,6 +8,8 @@ COPY HtmlToPdfService.Api/HtmlToPdfService.Api.csproj HtmlToPdfService.Api/
COPY HtmlToPdfService.Core/HtmlToPdfService.Core.csproj HtmlToPdfService.Core/
COPY HtmlToPdfService.Queue/HtmlToPdfService.Queue.csproj HtmlToPdfService.Queue/
COPY HtmlToPdfService.Infrastructure/HtmlToPdfService.Infrastructure.csproj HtmlToPdfService.Infrastructure/
COPY HtmlToPdfService.Client/HtmlToPdfService.Client.csproj HtmlToPdfService.Client/
COPY HtmlToPdfService.Tests/HtmlToPdfService.Tests.csproj HtmlToPdfService.Tests/
# 还原 NuGet 包
RUN dotnet restore HtmlToPdfService.sln
@ -67,13 +69,14 @@ WORKDIR /app
COPY --from=build /app/publish .
# 创建存储目录
RUN mkdir -p /app/files /app/logs && chmod -R 777 /app/files /app/logs
RUN mkdir -p /app/files /app/logs /app/config /app/chromium && chmod -R 777 /app/files /app/logs /app/config /app/chromium
# 设置环境变量
ENV ASPNETCORE_URLS=http://+:5000
ENV ASPNETCORE_ENVIRONMENT=Production
ENV DOTNET_RUNNING_IN_CONTAINER=true
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=false
ENV PUPPETEER_CACHE_DIR=/app/chromium
# 暴露端口
EXPOSE 5000

View File

@ -276,27 +276,20 @@ var logger = app.Services.GetRequiredService<ILogger<Program>>();
// 仅当已初始化时才预热浏览器池
if (isInitialized)
{
try
{
var browserPool = app.Services.GetRequiredService<IBrowserPool>();
logger.LogInformation("步骤 1/2: 准备 Chromium 浏览器引擎...");
var chromiumPath = await browserPool.EnsureChromiumReadyAsync(
progress => logger.LogInformation(" {Progress}", progress));
logger.LogInformation("步骤 2/2: 预热浏览器池...");
await browserPool.WarmUpAsync();
logger.LogInformation("浏览器池预热完成");
logger.LogInformation("========================================");
logger.LogInformation("服务准备就绪!");
logger.LogInformation("Swagger UI: http://localhost:5000/swagger");
logger.LogInformation("========================================");
}
catch (Exception ex)
{
logger.LogError(ex, "启动准备失败,服务可能无法正常工作");
}
var browserPool = app.Services.GetRequiredService<IBrowserPool>();
logger.LogInformation("步骤 1/2: 准备 Chromium 浏览器引擎...");
var chromiumPath = await browserPool.EnsureChromiumReadyAsync(
progress => logger.LogInformation(" {Progress}", progress));
logger.LogInformation("步骤 2/2: 预热浏览器池...");
await browserPool.WarmUpAsync();
logger.LogInformation("浏览器池预热完成");
logger.LogInformation("========================================");
logger.LogInformation("服务准备就绪!");
logger.LogInformation("Swagger UI: http://localhost:5000/swagger");
logger.LogInformation("========================================");
}
else
{

View File

@ -488,7 +488,37 @@ public class BrowserPool : IBrowserPool
_logger.LogInformation(message);
progress?.Invoke(message);
var browserFetcher = new BrowserFetcher();
// 使用固定缓存目录,方便 Docker volume 映射持久化
var cacheDir = Environment.GetEnvironmentVariable("PUPPETEER_CACHE_DIR")
?? Path.Combine(AppContext.BaseDirectory, "chromium");
Directory.CreateDirectory(cacheDir);
var fetcherOptions = new BrowserFetcherOptions { Path = cacheDir };
// 支持通过环境变量配置 Chromium 下载代理
var proxyUrl = Environment.GetEnvironmentVariable("CHROMIUM_DOWNLOAD_PROXY");
if (!string.IsNullOrWhiteSpace(proxyUrl))
{
_logger.LogInformation("使用代理下载 Chromium: {Proxy}", proxyUrl);
fetcherOptions.CustomFileDownload = async (url, destinationPath) =>
{
var handler = new System.Net.Http.HttpClientHandler
{
Proxy = new System.Net.WebProxy(proxyUrl),
UseProxy = true
};
using var httpClient = new System.Net.Http.HttpClient(handler);
httpClient.Timeout = TimeSpan.FromMinutes(10);
using var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
using var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None);
await response.Content.CopyToAsync(fileStream);
};
}
var browserFetcher = new BrowserFetcher(fetcherOptions);
var installedBrowsers = browserFetcher.GetInstalledBrowsers();
if (installedBrowsers.Any())

View File

@ -2,6 +2,7 @@ using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PuppeteerSharp;
using HtmlToPdfService.Core.Configuration;
using HtmlToPdfService.Core.Models;
using HtmlToPdfService.Core.Options;
using HtmlToPdfService.Core.Pool;
@ -13,18 +14,44 @@ namespace HtmlToPdfService.Core.Services;
/// </summary>
public class PuppeteerImageService : IImageService
{
private readonly PdfServiceOptions _options;
private readonly IOptions<PdfServiceOptions> _staticOptions;
private readonly IConfigurationManager? _configManager;
private readonly ILogger<PuppeteerImageService> _logger;
private readonly IBrowserPool _browserPool;
/// <summary>
/// 获取当前有效配置(优先使用动态配置)
/// </summary>
private PdfServiceOptions _options
{
get
{
if (_configManager?.IsInitialized == true)
{
try
{
var userConfig = _configManager.GetConfigurationAsync().GetAwaiter().GetResult();
return userConfig.ToPdfServiceOptions();
}
catch
{
// 动态配置读取失败时回退到静态配置
}
}
return _staticOptions.Value;
}
}
public PuppeteerImageService(
IOptions<PdfServiceOptions> options,
ILogger<PuppeteerImageService> logger,
IBrowserPool browserPool)
IBrowserPool browserPool,
IConfigurationManager? configManager = null)
{
_options = options.Value;
_staticOptions = options;
_logger = logger;
_browserPool = browserPool;
_configManager = configManager;
}
/// <inheritdoc />

View File

@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PuppeteerSharp;
using PuppeteerSharp.Media;
using HtmlToPdfService.Core.Configuration;
using HtmlToPdfService.Core.Models;
using HtmlToPdfService.Core.Options;
using HtmlToPdfService.Core.Pool;
@ -14,18 +15,44 @@ namespace HtmlToPdfService.Core.Services;
/// </summary>
public class PuppeteerPdfService : IPdfService
{
private readonly PdfServiceOptions _options;
private readonly IOptions<PdfServiceOptions> _staticOptions;
private readonly IConfigurationManager? _configManager;
private readonly ILogger<PuppeteerPdfService> _logger;
private readonly IBrowserPool _browserPool;
/// <summary>
/// 获取当前有效配置(优先使用动态配置)
/// </summary>
private PdfServiceOptions _options
{
get
{
if (_configManager?.IsInitialized == true)
{
try
{
var userConfig = _configManager.GetConfigurationAsync().GetAwaiter().GetResult();
return userConfig.ToPdfServiceOptions();
}
catch
{
// 动态配置读取失败时回退到静态配置
}
}
return _staticOptions.Value;
}
}
public PuppeteerPdfService(
IOptions<PdfServiceOptions> options,
ILogger<PuppeteerPdfService> logger,
IBrowserPool browserPool)
IBrowserPool browserPool,
IConfigurationManager? configManager = null)
{
_options = options.Value;
_staticOptions = options;
_logger = logger;
_browserPool = browserPool;
_configManager = configManager;
}
/// <inheritdoc />