From 27fb40ee265f550583dfb1bb9678218f728f2037 Mon Sep 17 00:00:00 2001 From: huanld Date: Mon, 18 May 2026 14:54:09 +0700 Subject: [PATCH] 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 --- README.md | 422 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5dbb341 --- /dev/null +++ b/README.md @@ -0,0 +1,422 @@ +# 📄 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](#tổng-quan) +- [Kiến trúc](#kiến-trúc) +- [Cài đặt](#cài-đặt) +- [API Endpoints](#api-endpoints) +- [Cách sử dụng](#cách-sử-dụng) +- [Code Signing](#code-signing) +- [Deployment](#deployment) +- [Troubleshooting](#troubleshooting) + +--- + +## 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`: + +```xml + + + +``` + +### 2. Syncfusion License + +Thêm vào `Program.cs`: + +```csharp +Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR_LICENSE_KEY"); +``` + +### 3. Service Registration (DI) + +```csharp +// Program.cs +builder.Services.AddScoped(); +``` + +### 4. Interface + +```csharp +public interface IExcelExportServices +{ + MemoryStream ConvertExcelToPdf(string excelFilePath); + MemoryStream ConvertExcelToPdf(string excelFilePath, string printArea); + Task ExportDeliveryNoteToPdf(Guid pkgpackageid, string TemplateType); +} +``` + +### 5. Implementation chính + +```csharp +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 + +```http +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 + +```http +GET /api/ExcelToPdf/export-pdf/{packageId}?templateType=Delivery_SB.xlsx +Authorization: Bearer {token} +``` + +**Response**: +```json +{ + "success": true, + "filePath": "/export/sb_20250518_143022_a1b2c3d4.pdf" +} +``` + +--- + +### 3. Convert Excel File sang PDF + +```http +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**: +```json +{ + "success": true, + "filePath": "/export/sb_20250518_143022_a1b2c3d4.pdf" +} +``` + +**Response error**: +```json +{ + "success": false, + "message": "File not found: C:\\path\\to\\file.xlsx" +} +``` + +--- + +## Cách sử dụng + +### JavaScript / Fetch API + +```javascript +// 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 + +```csharp +@inject IExcelExportServices ExcelExportServices +@inject NavigationManager NavigationManager + + + +@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 + +```csharp +// 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 + +```powershell +# 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 + +```powershell +# 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 + +```powershell +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 + +```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**: +```csharp +// Đặ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**: +```csharp +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 + +- [Syncfusion XlsIORenderer Docs](https://help.syncfusion.com/file-formats/xlsio/excel-to-pdf-conversion) +- [Syncfusion Excel to PDF – Print Area](https://help.syncfusion.com/file-formats/xlsio/excel-to-pdf-conversion#print-area) +- [ASP.NET Core File Results](https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types) +- [Azure Trusted Signing](https://learn.microsoft.com/en-us/azure/trusted-signing/) — xem repo [signing-tool](https://gittea.softs.business/huanld/signing-tool)