Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
9 posts found #excel
End to End: Bookstore Order Documents — Invoice PDF, Confirmation Email and the Monthly Excel Report as a Job Chain
Backend PART 09

End to End: Bookstore Order Documents — Invoice PDF, Confirmation Email and the Monthly Excel Report as a Job Chain

Connect the order documents in a two-step Hangfire chain: the invoice job writes the PDF to storage, the continuation puts the confirmation email in the outbox; each step's key is the very thing it writes, so repeats are harmless. In Bookstore only the invoice step was retried while storage was down, the same order placed twice gave one PDF and one email, and the monthly report is a recurring job on Istanbul time.

15 Nov 2026 11 min 6
A File Storage Abstraction: Local Disk, Azure Blob Storage and S3; Direct Uploads with Presigned/SAS URLs
Backend PART 08

A File Storage Abstraction: Local Disk, Azure Blob Storage and S3; Direct Uploads with Presigned/SAS URLs

Keep files behind one IFileStore: validate the path twice on the local disk, hand out SAS or presigned URLs that live a few minutes for downloads and uploads on Azure Blob and S3, and check an uploaded file only in the "complete" step, because storage looks at neither size nor content. Bookstore's invoices and covers were tested with Azurite and RustFS; metadata stays in the database.

15 Nov 2026 12 min 9
Image Processing in .NET: Resizing, Format Conversion, EXIF and Safe Processing with ImageSharp and SkiaSharp
Backend PART 07

Image Processing in .NET: Resizing, Format Conversion, EXIF and Safe Processing with ImageSharp and SkiaSharp

Validate an uploaded image by its first bytes and pixel count first, then apply the EXIF orientation, shrink it and save it as WebP without metadata. Bookstore's cover thumbnails are made with SkiaSharp (MIT); ImageSharp 4 stays in a Debug-only example, because its Release build needs a license key and it keeps metadata by default.

15 Nov 2026 10 min 10
Generating PDFs Part 2: HTML to PDF with Playwright, Reading and Merging PDFs and Which Approach When
Backend PART 06

Generating PDFs Part 2: HTML to PDF with Playwright, Reading and Merging PDFs and Which Approach When

Turn a document the designer built in HTML and CSS into a PDF with Playwright's headless Chromium: share the browser, open a context per document, turn JavaScript off and allow only your own fonts. Bookstore's institutional quote takes 38 milliseconds per document; merging and reading use PdfPig, because PDFsharp moved to the AGPL with 6.5.0.

15 Nov 2026 12 min 9
Generating PDFs Part 1: Invoices and Reports with QuestPDF, Fonts and Turkish Characters
Backend PART 05

Generating PDFs Part 1: Invoices and Reports with QuestPDF, Fonts and Turkish Characters

Build the invoice with QuestPDF's fluent layout, repeat the table header on every page, put page numbers in the footer and ship fonts with the app: since 2026.9 system fonts are off by default and a missing font or glyph throws. Bookstore's invoice prints Turkish characters correctly with the bundled Lato, in a container too; the Community License line is a declaration for companies under USD 1 million in annual revenue.

15 Nov 2026 12 min 8
Creating and Reading Excel Files: Formatted Reports with ClosedXML, Large Exports and the EPPlus License
Backend PART 04

Creating and Reading Excel Files: Formatted Reports with ClosedXML, Large Exports and the EPPlus License

Produce formatted reports with ClosedXML; write dates as DateTime, ISBNs as text and money through number formats, render into memory and send with filename* in Content-Disposition. For large exports ClosedXML uses 368 MB at 100,000 rows and OpenXmlWriter 56 MB, but System.IO.Packaging pushes the latter to 583 MB at a million; SpreadCheetah stays at 8 MB. EPPlus needs a per-developer license for commercial use.

15 Nov 2026 13 min 9
Email Templates and Reliable Delivery: Razor HtmlRenderer, Inline CSS, Outbox, Retry and Provider APIs
Backend PART 03

Email Templates and Reliable Delivery: Razor HtmlRenderer, Inline CSS, Outbox, Retry and Provider APIs

Write email HTML as a Razor component rendered to a string with HtmlRenderer, inline the CSS with PreMailer.Net, store the email as an outbox row in the same transaction as the business data, and retry on a schedule for 4xx while giving up on 5xx. In Bookstore the TR/EN approver reminder goes out once with the same Message-Id after two 451s from Mailpit's chaos mode, and an idempotency key stops the second run.

15 Nov 2026 12 min 9
Sending Email in .NET: SMTP with MailKit, TLS and Ports, Local Testing with Mailpit and the State of SmtpClient
Backend PART 02

Sending Email in .NET: SMTP with MailKit, TLS and Ports, Local Testing with Mailpit and the State of SmtpClient

In new .NET projects send email with MailKit rather than System.Net.Mail.SmtpClient: use StartTls on 587 and SslOnConnect on 465, avoid Auto, reuse the connection and read the password from a secret. We send Bookstore's order confirmation with HTML, plain text, an inline logo and an attached invoice to a STARTTLS Mailpit and show the MIME structure and the up to sixfold connection gap over 50 emails with real output.

15 Nov 2026 12 min 7
Documents and Notifications in the Backend: In the Request or a Background Job, Library Licenses and Abstractions
Backend PART 01

Documents and Notifications in the Backend: In the Request or a Background Job, Library Licenses and Abstractions

Render a small, fast document inside the request and stream it; accept slow, large or retryable work with 202 Accepted, hand it to a background job and email a link when it is ready. In Bookstore an 8-second report returns a 504 after 5 seconds on the synchronous path and a 202 in 16 ms on the asynchronous one; we track the QuestPDF, EPPlus and ImageSharp licenses, including ImageSharp 4's check that stops Release builds, with nuget-license in CI.

15 Nov 2026 13 min 6
Blog Map