Sage.FileWriter 1.0.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Sage.FileWriter --version 1.0.0.2
                    
NuGet\Install-Package Sage.FileWriter -Version 1.0.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Sage.FileWriter" Version="1.0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sage.FileWriter" Version="1.0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Sage.FileWriter" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sage.FileWriter --version 1.0.0.2
                    
#r "nuget: Sage.FileWriter, 1.0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Sage.FileWriter@1.0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sage.FileWriter&version=1.0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Sage.FileWriter&version=1.0.0.2
                    
Install as a Cake Tool

Sage.FileWriter

现代化的 .NET 文件写入库,提供直观的链式调用 API,简化文件保存流程,支持进度跟踪与多种文件存在处理策略。

主要特性

  • 链式调用 API,代码简洁清晰
  • 支持异步与同步保存
  • 文件存在策略:覆盖、跳过、重命名、抛异常
  • 进度回调,适合大文件写入可视化
  • 自动创建目录与完善的错误反馈
  • 支持 CancellationToken
  • 默认 UTF-8 编码,良好国际化支持
  • AOT支持
  • 可配置编码:UTF-8/UTF-8 BOM/UTF-16/UTF-32/ASCII/GB2312/GBK/Big5

安装

Package Manager

Install-Package Sage.FileWriter

.NET CLI

dotnet add package Sage.FileWriter

PackageReference

<PackageReference Include="Sage.FileWriter" Version="1.0.0" />

快速开始

using Sage.FileWriter;

// 最简单的用法
var result = await StringFileWriter
    .Content("你好,世界!")
    .Path(@"C:\\temp\\hello.txt")
    .SaveAsync();

if (result.Success)
{
    Console.WriteLine($"文件保存成功:{result.FinalFilePath}");
    Console.WriteLine($"文件大小:{result.FileSize} 字节");
}
else
{
    Console.WriteLine($"保存失败:{result.ErrorMessage}");
}

详细使用

1) 基础用法

异步保存示例:

// JSON 配置文件保存
var config = new { AppName = "MyApp", Version = "1.0.0" };
var json = JsonConvert.SerializeObject(config, Formatting.Indented);
var result = await StringFileWriter
    .Content(json)
    .Path(@"C:\\MyApp\\config.json")
    .SaveAsync();

同步保存示例:

// 日志文件保存
var logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - 应用启动";
var result = StringFileWriter
    .Content(logEntry)
    .Path(@"C:\\logs\\app.log")
    .Save(); // 同步方法

2) 文件路径与文件名配置

// 方式1:路径包含文件名
await StringFileWriter
    .Content("数据内容")
    .Path(@"C:\\data\\export.txt")
    .SaveAsync();

// 方式2:分别指定路径和文件名(文件名会覆盖路径中的文件名)
await StringFileWriter
    .Content("数据内容")
    .Path(@"C:\\data\\")
    .Filename("report.txt")
    .SaveAsync();

// 方式3:动态文件名
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
await StringFileWriter
    .Content(csvData)
    .Path(@"C:\\exports\\")
    .Filename($"销售报表_{timestamp}.csv")
    .SaveAsync();

3) 文件存在处理策略

直接覆盖:

var result = await StringFileWriter
    .Content("新内容")
    .Path(@"C:\\config\\app.json")
    .OnFileExists(path => FileExistsAction.Overwrite)
    .SaveAsync();

自动重命名:

var result = await StringFileWriter
    .Content("备份数据")
    .Path(@"C:\\backup\\data.txt")
    .OnFileExists(path => FileExistsAction.Rename) // 会生成 data_1.txt, data_2.txt 等
    .SaveAsync();
Console.WriteLine($"实际保存路径:{result.FinalFilePath}");

跳过保存:

var result = await StringFileWriter
    .Content("日志内容")
    .Path(@"C:\\logs\\today.log")
    .OnFileExists(path => FileExistsAction.Skip)
    .SaveAsync();
if (!result.Success)
{
    Console.WriteLine("文件已存在,跳过保存");
}

弹窗询问用户(WinForms):

var result = await StringFileWriter
    .Content(documentContent)
    .Path(@"C:\\documents\\重要文档.txt")
    .OnFileExists(filePath => 
    {
        var fileName = Path.GetFileName(filePath);
        var dialogResult = MessageBox.Show(
            $"文件 '{fileName}' 已存在!\n\n" +
            "点击 '是' 覆盖文件\n" +
            "点击 '否' 自动重命名\n" +
            "点击 '取消' 跳过保存",
            "文件已存在",
            MessageBoxButtons.YesNoCancel,
            MessageBoxIcon.Question);
        return dialogResult switch
        {
            DialogResult.Yes => FileExistsAction.Overwrite,
            DialogResult.No => FileExistsAction.Rename,
            _ => FileExistsAction.Skip
        };
    })
    .SaveAsync();

WPF 弹窗询问:

var result = await StringFileWriter
    .Content(xmlContent)
    .Path(selectedFilePath)
    .OnFileExists(filePath => 
    {
        var fileName = Path.GetFileName(filePath);
        var messageResult = MessageBox.Show(
            $"文件 '{fileName}' 已存在,是否覆盖?",
            "确认覆盖",
            MessageBoxButton.YesNoCancel,
            MessageBoxImage.Question);
        return messageResult switch
        {
            MessageBoxResult.Yes => FileExistsAction.Overwrite,
            MessageBoxResult.No => FileExistsAction.Rename,
            _ => FileExistsAction.Skip
        };
    })
    .SaveAsync();

4) 进度跟踪

控制台进度显示:

var largeContent = GenerateLargeText(); // 假设是很大的文本内容
var result = await StringFileWriter
    .Content(largeContent)
    .Path(@"C:\\temp\\large_file.txt")
    .OnProgress((bytesWritten, totalBytes, percentage) =>
    {
        Console.Write($"\r进度: {percentage:F1}% ({bytesWritten:N0}/{totalBytes:N0} 字节)");
    })
    .SaveAsync();
Console.WriteLine(); // 换行

WinForms 进度条:

// 假设有一个 ProgressBar 控件叫 progressBar1
var result = await StringFileWriter
    .Content(bigData)
    .Path(outputPath)
    .OnProgress((bytesWritten, totalBytes, percentage) =>
    {
        // 在 UI 线程更新进度条
        this.Invoke(() =>
        {
            progressBar1.Value = (int)percentage;
            labelStatus.Text = $"正在保存... {percentage:F1}%";
        });
    })
    .SaveAsync();

5) 错误处理

var result = await StringFileWriter
    .Content(sensitiveData)
    .Path(@"C:\\protected\\sensitive.txt")
    .OnFileExists(path => FileExistsAction.ThrowError)
    .SaveAsync();

if (!result.Success)
{
    switch (result.ErrorMessage)
    {
        case var msg when msg.Contains("Access denied"):
            MessageBox.Show("没有权限写入该位置,请选择其他文件夹或以管理员身份运行");
            break;
        case var msg when msg.Contains("Directory not found"):
            MessageBox.Show("目标文件夹不存在,将自动创建");
            break;
        case var msg when msg.Contains("File already exists"):
            MessageBox.Show("文件已存在且设置了抛出异常策略");
            break;
        default:
            MessageBox.Show($"保存失败:{result.ErrorMessage}");
            break;
    }
}

6) 取消操作

using var cts = new CancellationTokenSource();

// 5 秒后自动取消
cts.CancelAfter(TimeSpan.FromSeconds(5));

// 或者绑定到 UI 取消按钮
// buttonCancel.Click += (s, e) => cts.Cancel();

var result = await StringFileWriter
    .Content(hugeContent)
    .Path(@"C:\\temp\\huge_file.txt")
    .OnProgress((written, total, percent) =>
    {
        Console.WriteLine($"进度: {percent:F1}%");
        // 可以在这里检查用户是否要求取消
    })
    .SaveAsync(cts.Token);

if (!result.Success && result.ErrorMessage.Contains("cancelled"))
{
    Console.WriteLine("用户取消了保存操作");
}

7) 扩展方法

using Sage.FileWriter.Extensions;

// 字符串直接保存
string configJson = GetConfigurationJson();
var result = await configJson.SaveToFileAsync(@"C:\\config\\app.json");

// 快速保存方法
var quickResult = await StringFileWriterExtensions.QuickSaveAsync(
    content: logData,
    filePath: @"C:\\logs\\app.log",
    existsAction: FileExistsAction.Rename);

8) 实际场景示例

配置文件保存:

public async Task<bool> SaveUserSettingsAsync(UserSettings settings)
{
    var json = JsonConvert.SerializeObject(settings, Formatting.Indented);
    
    var result = await StringFileWriter
        .Content(json)
        .Path(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), 
                          "MyApp", "settings.json"))
        .OnFileExists(_ => FileExistsAction.Overwrite)
        .SaveAsync();
    
    return result.Success;
}

日志记录:

public async Task WriteLogAsync(string message, LogLevel level = LogLevel.Info)
{
    var logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{level}] {message}";
    var logPath = Path.Combine("logs", $"{DateTime.Now:yyyy-MM-dd}.log");
    
    await StringFileWriter
        .Content(logEntry + Environment.NewLine)
        .Path(logPath)
        .OnFileExists(_ => FileExistsAction.Overwrite) // 日志文件通常追加,这里演示覆盖
        .SaveAsync();
}

数据导出:

public async Task<string> ExportDataToCsvAsync(List<Customer> customers, string exportPath)
{
    var csv = new StringBuilder();
    csv.AppendLine("客户ID,姓名,邮箱,注册日期");
    
    foreach (var customer in customers)
    {
        csv.AppendLine($"{customer.Id},{customer.Name},{customer.Email},{customer.RegisterDate:yyyy-MM-dd}");
    }
    
    var result = await StringFileWriter
        .Content(csv.ToString())
        .Path(exportPath)
        .OnFileExists(path => 
        {
            // 导出时询问用户
            var choice = MessageBox.Show("导出文件已存在,是否覆盖?", "确认", 
                                       MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            return choice == DialogResult.Yes ? FileExistsAction.Overwrite : FileExistsAction.Rename;
        })
        .OnProgress((written, total, percent) =>
        {
            Console.WriteLine($"导出进度: {percent:F1}%");
        })
        .SaveAsync();
    
    return result.Success ? result.FinalFilePath : null;
}

9) 编码支持

支持多种编码,默认使用无 BOM 的 UTF-8。可以通过两种方式指定编码:

  • Content 中指定编码:
var result = await StringFileWriter
    .Content("你好,世界!", FileEncoding.UTF8WithBOM) // 指定 UTF-8 BOM
    .Path(@"C:\\temp\\hello_bom.txt")
    .SaveAsync();
  • 使用扩展方法时指定编码:
using Sage.FileWriter.Extensions;

// 以 GBK 编码保存
var res1 = await "中文内容".SaveToFileAsync(@"C:\\temp\\cn_gbk.txt", FileEncoding.GBK);

// 快速保存并设置编码与存在策略
var res2 = await StringFileWriterExtensions.QuickSaveAsync(
    content: "数据导出",
    filePath: @"C:\\exports\\data.txt",
    existsAction: FileExistsAction.Rename,
    encoding: FileEncoding.UTF16
);

写入结果中会包含实际使用的编码名称:

var result = await StringFileWriter
    .Content("text", FileEncoding.ASCII)
    .Path(@"C:\\temp\\ascii.txt")
    .SaveAsync();

Console.WriteLine($"编码: {result.Encoding}"); // 例如 "US-ASCII"

返回结果类型

所有操作都会返回 FileWriteResult,包含详细的结果信息:

public class FileWriteResult
{
    public bool Success { get; set; }           // 是否成功
    public string ErrorMessage { get; set; }    // 错误信息
    public string FinalFilePath { get; set; }   // 实际文件路径
    public long FileSize { get; set; }          // 文件大小
    public string Encoding { get; set; }        // 使用的编码格式名称
}

系统要求

  • .NET 8/9/10

开源协议

本项目采用 Apache License 2.0 开源协议,详见仓库根目录 LICENSE 文件。

贡献

欢迎提交 Issue 和 Pull Request!

更新日志

1.0.0

  • 首个版本发布
  • 支持链式调用 API
  • 异步与同步操作支持
  • 文件存在处理策略
  • 进度跟踪功能
  • 全面错误处理

如有问题或建议,请提交 Issue。

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0.5 110 1/14/2026
1.0.0.4 465 11/18/2025
1.0.0.3 340 11/12/2025
1.0.0.2 331 11/12/2025
1.0.0.1 325 11/10/2025
1.0.0 299 11/10/2025

新增文件存在追加写入功能