Files
exceltopdf-service/README.md
T
huanld 27fb40ee26 feat: initial commit - Excel to PDF Export Service documentation
- README.md: full API docs, architecture, code samples, deployment guide
- Syncfusion XlsIORenderer based conversion
- ASP.NET Core 8.0 service
- Code signing info: SPM.NewAPI.exe signed with Azure Trusted Signing
2026-05-18 14:54:09 +07:00

11 KiB
Raw Blame History

📄 Excel to PDF Export Service

ASP.NET Core API service xuất file Excel sang PDF sử dụng Syncfusion XlsIORenderer — tích hợp trong dự án SPM.NewAPI.

Mục lục


Tổng quan

ExcelExportServices (trong SPM.NewAPI) cung cấp chức năng:

  • Chuyển đổi file Excel (.xlsx) sang PDF với chất lượng cao
  • Tự động phát hiện vùng in (Print Area) hoặc dùng toàn bộ dữ liệu
  • Fit-to-page layout (FitToPagesWide = 1)
  • Auto-cleanup file sau 10 phút
  • REST API endpoints để tích hợp frontend

Tech stack:

Thành phần Version
ASP.NET Core 8.0
Syncfusion.XlsIO.Net.Core 21.1.37
Syncfusion.Pdf.Net.Core 21.1.37
Syncfusion.XlsIORenderer.Net.Core 21.1.37

Kiến trúc

HTTP Request
    │
    ▼
ExcelToPdfController
    │
    ├── GET /export-excel/{packageId}  → ExcelExportServices.ExportDeliveryNoteToExcel()
    │                                         └── Tạo file .xlsx từ template
    │
    ├── GET /export-pdf/{packageId}    → ExcelExportServices.ExportDeliveryNoteToPdf()
    │                                         ├── Tạo file .xlsx
    │                                         ├── ConvertExcelToPdf()
    │                                         └── Lưu file, auto-delete sau 10 phút
    │
    └── POST /convert-excel-to-pdf     → ExcelExportServices.ConvertExcelToPdf()
                                              ├── XlsIORenderer.ConvertToPDF()
                                              └── Trả về MemoryStream

Cài đặt

1. Package Dependencies

Thêm vào SPM.NewAPI.csproj:

<PackageReference Include="Syncfusion.XlsIO.Net.Core"       Version="21.1.37" />
<PackageReference Include="Syncfusion.Pdf.Net.Core"          Version="21.1.37" />
<PackageReference Include="Syncfusion.XlsIORenderer.Net.Core" Version="21.1.37" />

2. Syncfusion License

Thêm vào Program.cs:

Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR_LICENSE_KEY");

3. Service Registration (DI)

// Program.cs
builder.Services.AddScoped<IExcelExportServices, ExcelExportServices>();

4. Interface

public interface IExcelExportServices
{
    MemoryStream ConvertExcelToPdf(string excelFilePath);
    MemoryStream ConvertExcelToPdf(string excelFilePath, string printArea);
    Task<string> ExportDeliveryNoteToPdf(Guid pkgpackageid, string TemplateType);
}

5. Implementation chính

public MemoryStream ConvertExcelToPdf(string excelFilePath, string printArea = null)
{
    using var excelEngine = new ExcelEngine();
    var application = excelEngine.Excel;
    application.DefaultVersion = ExcelVersion.Xlsx;

    using var fileStream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read);
    var workbook = application.Workbooks.Open(fileStream);
    var worksheet = workbook.Worksheets[0];

    // Thiết lập layout
    var settings = new XlsIORendererSettings
    {
        LayoutOptions = LayoutOptions.Automatic
    };

    // Fit to width
    worksheet.PageSetup.FitToPagesWide = 1;

    // Áp dụng print area nếu có
    if (!string.IsNullOrEmpty(printArea))
        worksheet.PageSetup.PrintArea = printArea;

    // Convert
    var renderer = new XlsIORenderer();
    var pdfDocument = renderer.ConvertToPDF(workbook, settings);

    var outputStream = new MemoryStream();
    pdfDocument.Save(outputStream);
    outputStream.Position = 0;
    return outputStream;
}

API Endpoints

1. Export Excel

GET /api/ExcelToPdf/export-excel/{packageId}?templateType=Delivery_SB.xlsx
Authorization: Bearer {token}

Response (file download):

Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="export_20250518_143022_abc123.xlsx"

2. Export PDF

GET /api/ExcelToPdf/export-pdf/{packageId}?templateType=Delivery_SB.xlsx
Authorization: Bearer {token}

Response:

{
  "success": true,
  "filePath": "/export/sb_20250518_143022_a1b2c3d4.pdf"
}

3. Convert Excel File sang PDF

POST /api/ExcelToPdf/convert-excel-to-pdf
Content-Type: application/json
Authorization: Bearer {token}

{
  "excelFilePath": "C:\\path\\to\\file.xlsx",
  "printArea": "B2:F15"
}

Request body:

Field Type Bắt buộc Mô tả
excelFilePath string Đường dẫn tuyệt đối đến file Excel
printArea string Vùng in, ví dụ "B2:F15". Bỏ qua để dùng auto-detect

Response success:

{
  "success": true,
  "filePath": "/export/sb_20250518_143022_a1b2c3d4.pdf"
}

Response error:

{
  "success": false,
  "message": "File not found: C:\\path\\to\\file.xlsx"
}

Cách sử dụng

JavaScript / Fetch API

// Export PDF và mở trong tab mới
async function exportToPdf(packageId, templateType = 'Delivery_SB.xlsx') {
    const response = await fetch(
        `/api/ExcelToPdf/export-pdf/${packageId}?templateType=${templateType}`,
        { headers: { Authorization: `Bearer ${getToken()}` } }
    );
    const data = await response.json();

    if (data.success) {
        window.open(data.filePath, '_blank');
    } else {
        console.error('Export failed:', data.message);
        alert(`Export thất bại: ${data.message}`);
    }
}

Blazor Component

@inject IExcelExportServices ExcelExportServices
@inject NavigationManager NavigationManager

<button @onclick="() => ExportToPdf(packageId)">Export PDF</button>

@code {
    [Parameter] public Guid PackageId { get; set; }

    private async Task ExportToPdf(Guid packageId)
    {
        try
        {
            var pdfPath = await ExcelExportServices
                .ExportDeliveryNoteToPdf(packageId, "Delivery_SB.xlsx");
            NavigationManager.NavigateTo(pdfPath, true);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Export failed: {ex.Message}");
        }
    }
}

C# trực tiếp

// Inject service
private readonly IExcelExportServices _excelExportServices;

// Convert file có sẵn
using var pdfStream = _excelExportServices.ConvertExcelToPdf(
    @"C:\templates\Delivery_SB.xlsx",
    printArea: "A1:H50"  // optional
);

// Lưu ra file
await using var fileStream = new FileStream(@"C:\output\delivery.pdf", FileMode.Create);
await pdfStream.CopyToAsync(fileStream);

// Hoặc trả về từ Controller
return File(pdfStream, "application/pdf", "delivery.pdf");

Code Signing

Binary SPM.NewAPI.exe được ký số bằng Azure Trusted Signing để đảm bảo tính toàn vẹn.

Thông tin chữ ký

Thông số Giá trị
Signer CN=huanleducoutlook.onmicrosoft.com
Organization huanleducoutlook.onmicrosoft.com
Trust Type Private Trust
Timestamp Authority http://timestamp.acs.microsoft.com

Ký sau khi build

# Build project
dotnet publish "SPM.NewAPI.csproj" -c Release -o ./publish

# Ký binary
powershell -ExecutionPolicy Bypass -File "d:\Code\LibreOfffice\core\sign-app.ps1" `
    -FilePath ".\publish\SPM.NewAPI.exe"

# Kiểm tra chữ ký
Get-AuthenticodeSignature ".\publish\SPM.NewAPI.exe" | Format-List Status, SignerCertificate

Build & Sign script tự động

# build-and-sign.ps1
param(
    [string]$Configuration = "Release",
    [string]$OutputPath    = ".\publish"
)

$projectFile = "SPM.NewAPI.csproj"
$signScript  = "d:\Code\LibreOfffice\core\sign-app.ps1"

Write-Host "[1/3] Building..."
dotnet publish $projectFile -c $Configuration -o $OutputPath
if ($LASTEXITCODE -ne 0) { throw "Build failed" }

Write-Host "[2/3] Signing..."
Get-ChildItem $OutputPath -Filter "*.exe" | ForEach-Object {
    powershell -ExecutionPolicy Bypass -File $signScript -FilePath $_.FullName
    if ($LASTEXITCODE -ne 0) { throw "Signing failed: $($_.Name)" }
}

Write-Host "[3/3] Done! Published to: $OutputPath"

Deployment

Publish

dotnet publish SPM.NewAPI.csproj \
    -c Release \
    -r win-x64 \
    --self-contained false \
    -o C:\inetpub\wwwroot\spm-api

IIS Setup

  1. Cài ASP.NET Core Runtime 8.x (Hosting Bundle)
  2. Tạo Application Pool: No Managed Code
  3. Trỏ Physical path đến thư mục publish
  4. Cài đặt quyền Read/Write cho thư mục wwwroot/export/

appsettings.Production.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "SyncfusionLicenseKey": "YOUR_LICENSE_KEY_HERE",
  "ExportSettings": {
    "OutputDirectory": "wwwroot/export",
    "AutoDeleteMinutes": 10
  }
}

File Management

  • Output directory: wwwroot/export/
  • Naming format: sb_yyyyMMdd_HHmmss_randomguid.pdf
  • Auto-delete: 10 phút sau khi tạo
  • Cleanup job: Background service chạy mỗi 5 phút

Troubleshooting

Syncfusion.XlsIORenderer.ConvertToPDF ném exception

Nguyên nhân: License key không hợp lệ hoặc chưa đăng ký.
Giải pháp:

// Đặt ở Program.cs trước khi app chạy
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR_KEY");

PDF layout bị vỡ / tràn trang

Nguyên nhân: Print area không được set hoặc data quá nhiều cột.
Giải pháp:

worksheet.PageSetup.FitToPagesWide  = 1;
worksheet.PageSetup.FitToPagesTall  = 0; // auto height
worksheet.PageSetup.Orientation     = ExcelPageOrientation.Landscape;

File không tự động xóa

Nguyên nhân: Background service cleanup bị lỗi hoặc quyền truy cập file.
Giải pháp: Kiểm tra log và quyền của Application Pool trên thư mục wwwroot/export/.

File not found khi convert

Nguyên nhân: Path sử dụng backslash trong JSON bị escape sai.
Giải pháp: Dùng double backslash \\ trong JSON hoặc forward slash /.


Tham khảo