跳至页脚内容
视频

如何使用Razor在C#中无头地将CSHTML转换为PDF

Fluid是一个.NET库(在NuGet上作为Fluid.Core发布,由Sebastien Ros编写,采用MIT许可)实现了Liquid模板语言,为开发者提供了一种灵活的方式来渲染动态模板并将内容与表现逻辑分离。 Fluid在生成动态文本输出时很有效,但它不直接支持PDF生成——开发者必须集成一个额外的PDF库来将HTML输出转换为PDF文档。 这种双库方法带来了复杂性,而许多开发团队都希望消除这种复杂性。

本指南提供了从使用外部 PDF 库的 Fluid(模板化)到IronPDF的完整迁移路径,为评估这一过渡的 .NET 专业开发人员提供了分步说明、代码比较和实用示例。

为什么要从 Fluid(模板化)迁移到 IronPDF.

Fluid 是一个基于 Liquid 的优秀模板引擎,但将其用于 PDF 生成会引入显著的复杂性:

双库依赖: Fluid 只生成 HTML——你需要一个单独的 PDF 库(wkhtmltopdf、PuppeteerSharp 等)来创建 PDF,这使得你的依赖项和维护负担翻倍。

集成复杂性:协调两个库意味着管理两套配置、错误处理和更新。 当出现故障时,调试变得更具挑战性。

Liquid语法学习曲线:开发人员必须学习Liquid模板语法({{ }}, {% %}),而C#已经内置了强大的字符串处理功能。

PDF 控制有限:您的 PDF 输出质量取决于您选择与 Fluid 搭配使用的 PDF 库,而不是专用的渲染引擎。

调试挑战:模板生成或 PDF 生成阶段都可能出现错误,这使得故障排除比使用单一集成解决方案更加困难。

线程安全问题:TemplateContext不是线程安全的,需要在并发应用中小心管理。

IronPDFvs Fluid(模板化):功能比较

了解架构差异有助于技术决策者评估迁移投资:

方面 流体 + PDF 库 IronPDF
依赖关系 2 个以上软件包(Fluid + PDF 库) 单个软件包
模板 Liquid语法({{ }} C# 字符串插值或 Razor
PDF 生成 需要外部库 内置 Chromium 引擎
CSS支持 取决于 PDF 库 带有 Flexbox/Grid 的完整 CSS3
JavaScript语言 取决于 PDF 库 完全支持 JavaScript
线程安全 模板上下文不是线程安全的 ChromePdfRenderer 是线程安全的
学习曲线 Liquid + PDF 库 API HTML/CSS(网络标准)
错误处理 两个错误源 单一错误源

快速入门:从 Fluid 迁移到 IronPDF.

迁移工作可以通过以下基本步骤立即开始。

步骤 1:替换 NuGet 软件包

移除 Fluid 和任何外部 PDF 库:

# Remove Fluid and external PDF library
dotnet remove package Fluid.Core
dotnet remove package WkHtmlToPdf-DotNet  # or whatever PDF library you used
dotnet remove package PuppeteerSharp       # if used
# Remove Fluid and external PDF library
dotnet remove package Fluid.Core
dotnet remove package WkHtmlToPdf-DotNet  # or whatever PDF library you used
dotnet remove package PuppeteerSharp       # if used
SHELL

安装 IronPDF:

# InstallIronPDF(all-in-one solution)
dotnet add package IronPdf
# InstallIronPDF(all-in-one solution)
dotnet add package IronPdf
SHELL

步骤 2:更新命名空间

将Fluid命名空间替换为IronPDF:

// Before (Fluid + external PDF library)
using Fluid;
using Fluid.Values;
using SomeExternalPdfLibrary;

// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;  // For RenderingOptions
// Before (Fluid + external PDF library)
using Fluid;
using Fluid.Values;
using SomeExternalPdfLibrary;

// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;  // For RenderingOptions
Imports Fluid
Imports Fluid.Values
Imports SomeExternalPdfLibrary

Imports IronPdf
Imports IronPdf.Rendering ' For RenderingOptions
$vbLabelText   $csharpLabel

步骤 3:初始化许可证

在应用程序启动时添加许可证初始化:

IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

代码迁移示例

将基本 HTML 转换为 PDF.

最基本的操作揭示了这些方法之间的关键区别。

流畅的方法:

// NuGet: Install-Package Fluid.Core
using Fluid;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse("<html><body><h1>Hello {{name}}!</h1></body></html>");
        var context = new TemplateContext();
        context.SetValue("name", "World");
        var html = await template.RenderAsync(context);

        // Fluid only generates HTML - you'd need another library to convert to PDF
        File.WriteAllText("output.html", html);
    }
}
// NuGet: Install-Package Fluid.Core
using Fluid;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse("<html><body><h1>Hello {{name}}!</h1></body></html>");
        var context = new TemplateContext();
        context.SetValue("name", "World");
        var html = await template.RenderAsync(context);

        // Fluid only generates HTML - you'd need another library to convert to PDF
        File.WriteAllText("output.html", html);
    }
}
Imports Fluid
Imports System.IO
Imports System.Threading.Tasks

Module Program
    Async Function Main() As Task
        Dim parser As New FluidParser()
        Dim template = parser.Parse("<html><body><h1>Hello {{name}}!</h1></body></html>")
        Dim context As New TemplateContext()
        context.SetValue("name", "World")
        Dim html = Await template.RenderAsync(context)

        ' Fluid only generates HTML - you'd need another library to convert to PDF
        File.WriteAllText("output.html", html)
    End Function
End Module
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var html = "<html><body><h1>Hello World!</h1></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var html = "<html><body><h1>Hello World!</h1></body></html>";
        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("output.pdf");
    }
}
Imports IronPdf
Imports System

Class Program
    Shared Sub Main()
        Dim renderer = New ChromePdfRenderer()
        Dim html = "<html><body><h1>Hello World!</h1></body></html>"
        Dim pdf = renderer.RenderHtmlAsPdf(html)
        pdf.SaveAs("output.pdf")
    End Sub
End Class
$vbLabelText   $csharpLabel

Fluid需要创建一个SetValue(),异步渲染以获取HTML,然后写入文件——这仍然不是PDF。 代码中的注释明确指出 "Fluid 只能生成 HTML - 您需要另一个库来转换为 PDF"。

IronPDF消除了这种复杂性:创建一个渲染器,调用RenderHtmlAsPdf(),直接保存为PDF。 无中间 HTML 文件,无附加库。

有关 HTML 转 PDF 的高级应用场景,请参阅 HTML 转 PDF 指南

带动态数据的发票模板

带有多个变量的文档模板可以清楚地显示模板模式的差异。

流畅的方法:

// NuGet: Install-Package Fluid.Core
using Fluid;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse(@"
            <html><body>
                <h1>Invoice #{{invoiceNumber}}</h1>
                <p>Date: {{date}}</p>
                <p>Customer: {{customer}}</p>
                <p>Total: ${{total}}</p>
            </body></html>");

        var context = new TemplateContext();
        context.SetValue("invoiceNumber", "12345");
        context.SetValue("date", DateTime.Now.ToShortDateString());
        context.SetValue("customer", "John Doe");
        context.SetValue("total", 599.99);

        var html = await template.RenderAsync(context);
        // Fluid outputs HTML - requires additional PDF library
        File.WriteAllText("invoice.html", html);
    }
}
// NuGet: Install-Package Fluid.Core
using Fluid;
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse(@"
            <html><body>
                <h1>Invoice #{{invoiceNumber}}</h1>
                <p>Date: {{date}}</p>
                <p>Customer: {{customer}}</p>
                <p>Total: ${{total}}</p>
            </body></html>");

        var context = new TemplateContext();
        context.SetValue("invoiceNumber", "12345");
        context.SetValue("date", DateTime.Now.ToShortDateString());
        context.SetValue("customer", "John Doe");
        context.SetValue("total", 599.99);

        var html = await template.RenderAsync(context);
        // Fluid outputs HTML - requires additional PDF library
        File.WriteAllText("invoice.html", html);
    }
}
Imports Fluid
Imports System
Imports System.IO
Imports System.Threading.Tasks

Module Program
    Async Function Main() As Task
        Dim parser As New FluidParser()
        Dim template = parser.Parse("
            <html><body>
                <h1>Invoice #{{invoiceNumber}}</h1>
                <p>Date: {{date}}</p>
                <p>Customer: {{customer}}</p>
                <p>Total: ${{total}}</p>
            </body></html>")

        Dim context As New TemplateContext()
        context.SetValue("invoiceNumber", "12345")
        context.SetValue("date", DateTime.Now.ToShortDateString())
        context.SetValue("customer", "John Doe")
        context.SetValue("total", 599.99)

        Dim html = Await template.RenderAsync(context)
        ' Fluid outputs HTML - requires additional PDF library
        File.WriteAllText("invoice.html", html)
    End Function
End Module
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var invoiceNumber = "12345";
        var date = DateTime.Now.ToShortDateString();
        var customer = "John Doe";
        var total = 599.99;

        var html = $@"
            <html><body>
                <h1>Invoice #{invoiceNumber}</h1>
                <p>Date: {date}</p>
                <p>Customer: {customer}</p>
                <p>Total: ${total}</p>
            </body></html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("invoice.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var invoiceNumber = "12345";
        var date = DateTime.Now.ToShortDateString();
        var customer = "John Doe";
        var total = 599.99;

        var html = $@"
            <html><body>
                <h1>Invoice #{invoiceNumber}</h1>
                <p>Date: {date}</p>
                <p>Customer: {customer}</p>
                <p>Total: ${total}</p>
            </body></html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("invoice.pdf");
    }
}
Imports IronPdf
Imports System

Module Program
    Sub Main()
        Dim renderer As New ChromePdfRenderer()
        Dim invoiceNumber As String = "12345"
        Dim [date] As String = DateTime.Now.ToShortDateString()
        Dim customer As String = "John Doe"
        Dim total As Double = 599.99

        Dim html As String = $"
            <html><body>
                <h1>Invoice #{invoiceNumber}</h1>
                <p>Date: {[date]}</p>
                <p>Customer: {customer}</p>
                <p>Total: ${total}</p>
            </body></html>"

        Dim pdf = renderer.RenderHtmlAsPdf(html)
        pdf.SaveAs("invoice.pdf")
    End Sub
End Module
$vbLabelText   $csharpLabel

Fluid使用Liquid的context.SetValue()。 注释明确指出"Fluid输出HTML - 需要额外的PDF库。" IronPDF使用标准C#字符串插值($"{variable}")——开发人员已经熟悉的语法——并直接输出至PDF。

探索 IronPDF 教程,了解更多文档生成模式。

使用循环的动态数据

带有集合和循环的模板展示了控制流的差异。

流畅的方法:

// NuGet: Install-Package Fluid.Core
using Fluid;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse(@"
            <html><body>
                <h1>{{title}}</h1>
                <ul>
                {% for item in items %}
                    <li>{{item}}</li>
                {% endfor %}
                </ul>
            </body></html>");

        var context = new TemplateContext();
        context.SetValue("title", "My List");
        context.SetValue("items", new[] { "Item 1", "Item 2", "Item 3" });

        var html = await template.RenderAsync(context);
        // Fluid generates HTML only - separate PDF conversion needed
        File.WriteAllText("template-output.html", html);
    }
}
// NuGet: Install-Package Fluid.Core
using Fluid;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var parser = new FluidParser();
        var template = parser.Parse(@"
            <html><body>
                <h1>{{title}}</h1>
                <ul>
                {% for item in items %}
                    <li>{{item}}</li>
                {% endfor %}
                </ul>
            </body></html>");

        var context = new TemplateContext();
        context.SetValue("title", "My List");
        context.SetValue("items", new[] { "Item 1", "Item 2", "Item 3" });

        var html = await template.RenderAsync(context);
        // Fluid generates HTML only - separate PDF conversion needed
        File.WriteAllText("template-output.html", html);
    }
}
Imports Fluid
Imports System.Collections.Generic
Imports System.IO
Imports System.Threading.Tasks

Class Program
    Shared Async Function Main() As Task
        Dim parser As New FluidParser()
        Dim template = parser.Parse("
            <html><body>
                <h1>{{title}}</h1>
                <ul>
                {% for item in items %}
                    <li>{{item}}</li>
                {% endfor %}
                </ul>
            </body></html>")

        Dim context As New TemplateContext()
        context.SetValue("title", "My List")
        context.SetValue("items", New String() {"Item 1", "Item 2", "Item 3"})

        Dim html = Await template.RenderAsync(context)
        ' Fluid generates HTML only - separate PDF conversion needed
        File.WriteAllText("template-output.html", html)
    End Function
End Class
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var title = "My List";
        var items = new[] { "Item 1", "Item 2", "Item 3" };

        var html = $@"
            <html><body>
                <h1>{title}</h1>
                <ul>";

        foreach (var item in items)
        {
            html += $"<li>{item}</li>";
        }

        html += "</ul></body></html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("template-output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var title = "My List";
        var items = new[] { "Item 1", "Item 2", "Item 3" };

        var html = $@"
            <html><body>
                <h1>{title}</h1>
                <ul>";

        foreach (var item in items)
        {
            html += $"<li>{item}</li>";
        }

        html += "</ul></body></html>";

        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("template-output.pdf");
    }
}
Imports IronPdf
Imports System
Imports System.Collections.Generic

Module Program
    Sub Main()
        Dim renderer As New ChromePdfRenderer()
        Dim title As String = "My List"
        Dim items As String() = {"Item 1", "Item 2", "Item 3"}

        Dim html As String = $"
            <html><body>
                <h1>{title}</h1>
                <ul>"

        For Each item As String In items
            html += $"<li>{item}</li>"
        Next

        html += "</ul></body></html>"

        Dim pdf = renderer.RenderHtmlAsPdf(html)
        pdf.SaveAs("template-output.pdf")
    End Sub
End Module
$vbLabelText   $csharpLabel

Fluid使用Liquid的{% for item in items %}...{% endfor %}语法——开发人员必须学习的一种模板语言。 注释指出"Fluid仅生成HTML - 需要单独的PDF转换。" IronPDF使用标准C# foreach 循环——无需学习新语法——直接输出至PDF。

Fluid API 到IronPDF映射参考

这种映射通过显示直接的 API 对应关系来加速迁移:

核心类映射

流体类 IronPDF 同等产品
FluidParser 不适用
FluidTemplate 不适用
TemplateContext C# 对象/字符串
TemplateOptions RenderingOptions
FluidValue 本地 C# 类型
外部 PDF 类 ChromePdfRenderer

方法映射

流体方法 IronPDF 同等产品
new FluidParser() new ChromePdfRenderer()
parser.Parse(source) 不适用
template.RenderAsync(context) renderer.RenderHtmlAsPdf(html)
context.SetValue("key", value) var key = value;

Liquid语法到C#映射

液体语法 C# 对等语
{{variable}} $"{variable}"
{% for item in items %} foreach (var item in items)
{% if condition %} if (condition)
{{x |上例}} x.ToUpper()
{{x |date: '%Y-%m-%d'}} x.ToString("yyyy-MM-dd")
{{x |number_with_precision: 2}} x.ToString("F2")

常见迁移问题和解决方案

问题 1:液体语法转换

Fluid:使用{% control %}语法。

解决方案:替换为 C# 字符串插值和控制流:

// Liquid: {{name | upcase}}
// C#: $"{name.ToUpper()}"

// Liquid: {% for item in items %}{{item}}{% endfor %}
// C#: foreach (var item in items) { html += $"{item}"; }
// Liquid: {{name | upcase}}
// C#: $"{name.ToUpper()}"

// Liquid: {% for item in items %}{{item}}{% endfor %}
// C#: foreach (var item in items) { html += $"{item}"; }
' Liquid: {{name | upcase}}
' C#: $"{name.ToUpper()}"
Dim result As String = name.ToUpper()

' Liquid: {% for item in items %}{{item}}{% endfor %}
' C#: foreach (var item in items) { html += $"{item}"; }
For Each item In items
    html &= item.ToString()
Next
$vbLabelText   $csharpLabel

问题 2:模板上下文变量

Fluid:使用context.SetValue("key", value)传递数据。

解决方案:使用标准 C# 变量:

// Before (Fluid)
var context = new TemplateContext();
context.SetValue("customer", customerName);

// After (IronPDF)
var customer = customerName;
var html = $"<p>Customer: {customer}</p>";
// Before (Fluid)
var context = new TemplateContext();
context.SetValue("customer", customerName);

// After (IronPDF)
var customer = customerName;
var html = $"<p>Customer: {customer}</p>";
' Before (Fluid)
Dim context As New TemplateContext()
context.SetValue("customer", customerName)

' After (IronPDF)
Dim customer = customerName
Dim html = $"<p>Customer: {customer}</p>"
$vbLabelText   $csharpLabel

第 3 期:线程安全

Fluid:TemplateContext不是线程安全的,需要在并发应用中小心管理。

解决方案:ChromePdfRenderer是线程安全的,可以在线程间共享。

// Thread-safe usage
private static readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();

public byte[] GeneratePdf(string html)
{
    var pdf = _renderer.RenderHtmlAsPdf(html);
    return pdf.BinaryData;
}
// Thread-safe usage
private static readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();

public byte[] GeneratePdf(string html)
{
    var pdf = _renderer.RenderHtmlAsPdf(html);
    return pdf.BinaryData;
}
' Thread-safe usage
Private Shared ReadOnly _renderer As New ChromePdfRenderer()

Public Function GeneratePdf(html As String) As Byte()
    Dim pdf = _renderer.RenderHtmlAsPdf(html)
    Return pdf.BinaryData
End Function
$vbLabelText   $csharpLabel

第 4 期:两阶段错误处理

流畅:错误可能发生在模板阶段或 PDF 生成阶段。

解决方案:IronPDF只有一个错误源:

try
{
    var pdf = renderer.RenderHtmlAsPdf(html);
    pdf.SaveAs("output.pdf");
}
catch (Exception ex)
{
    // Single point of failure—easier debugging
    Console.WriteLine($"PDF generation failed: {ex.Message}");
}
try
{
    var pdf = renderer.RenderHtmlAsPdf(html);
    pdf.SaveAs("output.pdf");
}
catch (Exception ex)
{
    // Single point of failure—easier debugging
    Console.WriteLine($"PDF generation failed: {ex.Message}");
}
Try
    Dim pdf = renderer.RenderHtmlAsPdf(html)
    pdf.SaveAs("output.pdf")
Catch ex As Exception
    ' Single point of failure—easier debugging
    Console.WriteLine($"PDF generation failed: {ex.Message}")
End Try
$vbLabelText   $csharpLabel

流体迁移核对表

迁移前任务

审核您的代码库,确定所有 Fluid 使用情况:

# Find all Fluid references
grep -r "FluidParser\|FluidTemplate\|TemplateContext\|using Fluid" --include="*.cs" --include="*.csproj" .

# Find Liquid template files
find . -name "*.liquid" -o -name "*.html" | xargs grep -l "{{"
# Find all Fluid references
grep -r "FluidParser\|FluidTemplate\|TemplateContext\|using Fluid" --include="*.cs" --include="*.csproj" .

# Find Liquid template files
find . -name "*.liquid" -o -name "*.html" | xargs grep -l "{{"
SHELL

记录所有模板:文件位置、使用的变量、循环和条件以及外部 PDF 库配置。

代码更新任务

1.删除 Fluid.Core NuGet 软件包 2.删除外部 PDF 库包

  1. 安装IronPDF NuGet包
  2. 将命名空间导入从IronPdf
  3. 转换$"{variable}"
  4. {% for item in collection %}转换为C# foreach
  5. {% if condition %}转换为C# if语句 8.将 Liquid 过滤器转换为 C# 方法(例如,...|upcase.ToUpper())
  6. FluidParser
  7. 用直接C#变量替换TemplateContext.SetValue() 11.删除外部 PDF 库调用 12.在启动时添加IronPDF许可证初始化功能

迁移后测试

迁移后,验证这些方面:

  • 验证 PDF 输出是否符合预期
  • 测试所有模板变体是否能正确呈现
  • 检查图像和样式是否正确显示
  • 验证分页符是否正确
  • 使用各种数据大小进行测试
  • 性能测试与 Fluid + 外部库
  • 测试并发场景中的线程安全

清理任务

  • 删除.liquid模板文件(如果不再需要)
  • 删除与 Fluid 相关的辅助代码
  • 更新文档
  • 清理未使用的依赖关系

迁移到IronPDF的主要优势

从使用外部 PDF 库的 Fluid(模板化)转向IronPDF具有几个关键优势:

单包解决方案:消除对两个库的依赖。IronPDF在一个软件包中同时处理模板制作(通过 HTML/CSS)和 PDF 生成。

无需学习新语法:使用标准的 C# 字符串插值和控制流,而不是学习 Liquid 模板语法。

线程安全渲染:TemplateContext,简化了并发PDF生成。

Chromium渲染引擎:基于Chromium的渲染支持现代CSS(包括Flexbox和Grid)和JavaScript执行。

单一错误源:只需对一个库进行故障排除,而无需在模板和 PDF 生成阶段之间进行协调,从而简化了调试过程。

广泛的.NET覆盖:IronPDF支持.NET Framework 4.6.2+、.NET Core 2.0+、.NET Standard 2.0、以及.NET 5到.NET 10。

请注意Fluid、PuppeteerSharp和wkhtmltopdf是其各自所有者的商标。 本站与这些项目或其维护者没有关联、认可或赞助。 所有产品名称、徽标和品牌均为各自所有者的财产。 比较仅供参考,反映撰写时公开可用的信息。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我