You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多流合并序列化优化:小文件备份场景替代JsonTextWriter方案咨询

Great question—dealing with thousands of tiny files is a classic performance pain point, and ditching JSON (which adds unnecessary text overhead, especially for binary data) is absolutely the right call. Here are three high-performance approaches tailored to your scenario, ordered by flexibility/performance tradeoff:

1. Custom Binary Format (极致性能 & 最小体积)

This is the fastest and most space-efficient option because you eliminate all overhead from generic serialization libraries. You define a simple, compact structure for each file entry, so you’re only writing exactly what you need.

Structure Design

I’d recommend a straightforward binary layout for each entry:

  • 4-byte integer: Length of the filename (UTF-8 encoded)
  • Variable bytes: Filename (UTF-8)
  • 4-byte integer: Length of the file data
  • Variable bytes: File data (raw binary)
    Plus a leading 4-byte integer to store the total number of files (makes parsing easier later).

Code Example

using System.IO;
using System.Text;

// Writing the backup
using (var outputStream = new FileStream("backup.bin", FileMode.Create))
using (var writer = new BinaryWriter(outputStream, Encoding.UTF8, leaveOpen: false))
{
    // Write total file count first
    writer.Write(models.Count);

    foreach (var model in models)
    {
        // Write filename
        var nameBytes = Encoding.UTF8.GetBytes(model.Name);
        writer.Write(nameBytes.Length);
        writer.Write(nameBytes);

        // Write file data
        writer.Write((int)model.Data.Length);
        model.Data.CopyTo(outputStream);
        // Reset stream position in case it's reused later
        model.Data.Position = 0;
    }
}

// Reading the backup (for reference)
using (var inputStream = new FileStream("backup.bin", FileMode.Open))
using (var reader = new BinaryReader(inputStream, Encoding.UTF8))
{
    int fileCount = reader.ReadInt32();
    for (int i = 0; i < fileCount; i++)
    {
        int nameLength = reader.ReadInt32();
        string name = Encoding.UTF8.GetString(reader.ReadBytes(nameLength));
        
        int dataLength = reader.ReadInt32();
        byte[] data = reader.ReadBytes(dataLength);
        Stream dataStream = new MemoryStream(data);
        
        // Recreate your Model instance here
        var restoredModel = new Model { Name = name, Data = dataStream };
    }
}

Pros & Cons

  • ✅ Zero overhead, fastest I/O and smallest output size
  • ✅ Full control over the format
  • ❌ No built-in tooling to view the backup (you’ll need your own parser)
  • ❌ Manual maintenance of serialization/deserialization logic

2. ZipArchive with No Compression (Standard Format & Ease of Use)

If you want a standard, tool-compatible format (so you can open the backup with any zip utility) without the overhead of compression (which is useless for tiny files—sometimes even increases size), this is a great middle ground. .NET’s built-in ZipArchive is optimized for streaming, so it won’t bog down your performance.

Code Example

using System.IO;
using System.IO.Compression;

using (var outputStream = new FileStream("backup.zip", FileMode.Create))
using (var archive = new ZipArchive(outputStream, ZipArchiveMode.Create))
{
    foreach (var model in models)
    {
        // Create an entry with the file's name, disable compression
        var entry = archive.CreateEntry(model.Name, CompressionLevel.NoCompression);
        
        using (var entryStream = entry.Open())
        {
            model.Data.CopyTo(entryStream);
            // Reset stream position for reuse
            model.Data.Position = 0;
        }
    }
}

Pros & Cons

  • ✅ Standard zip format—use any zip tool to inspect contents
  • ✅ Minimal code, no custom logic needed
  • ✅ Streaming support keeps memory usage low
  • ❌ Slight overhead compared to custom binary (zip headers add a few bytes per entry)
  • ❌ Compression is counterproductive here, so make sure to disable it

3. Binary Serialization with MessagePack (Mature Library & Low Maintenance)

If you prefer using a battle-tested binary serialization library instead of rolling your own, MessagePack is a fantastic choice—it’s way faster than JSON and produces compact output. Since you can’t modify the Model class, you can create a custom formatter to only serialize the Name and Data properties you care about.

Setup

First, install the MessagePack NuGet package:

Install-Package MessagePack

Code Example

using MessagePack;
using System.IO;

// Configure custom resolver to handle the Model class
var options = MessagePackSerializerOptions.Standard.WithResolver(new ModelResolver());

// Write the backup
using (var outputStream = new FileStream("backup.msgpack", FileMode.Create))
{
    MessagePackSerializer.Serialize(outputStream, models, options);
}

// Custom resolver and formatter
public class ModelResolver : IMessagePackFormatterResolver
{
    public static readonly ModelResolver Instance = new ModelResolver();

    public IMessagePackFormatter<T> GetFormatter<T>()
    {
        if (typeof(T) == typeof(Model))
        {
            return (IMessagePackFormatter<T>)new ModelFormatter();
        }
        // Fall back to standard resolver for other types
        return StandardResolver.Instance.GetFormatter<T>();
    }

    private class ModelFormatter : IMessagePackFormatter<Model>
    {
        public void Serialize(ref MessagePackWriter writer, Model value, MessagePackSerializerOptions options)
        {
            // Write a map with 2 entries: Name and Data
            writer.WriteMapHeader(2);
            writer.Write("Name");
            writer.Write(value.Name);
            
            writer.Write("Data");
            // Copy stream to memory and write as byte array
            using (var ms = new MemoryStream())
            {
                value.Data.CopyTo(ms);
                writer.Write(ms.ToArray());
                value.Data.Position = 0;
            }
        }

        public Model Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
        {
            reader.ReadMapHeader();
            var model = new Model();
            
            while (reader.ReadString() is string key)
            {
                switch (key)
                {
                    case "Name":
                        model.Name = reader.ReadString();
                        break;
                    case "Data":
                        var bytes = reader.ReadBytes();
                        model.Data = new MemoryStream(bytes);
                        break;
                }
            }
            return model;
        }
    }
}

Pros & Cons

  • ✅ Mature, optimized library with great performance
  • ✅ No manual binary parsing logic
  • ✅ Compact output (close to custom binary)
  • ❌ Slight overhead compared to custom binary
  • ❌ Requires adding a third-party dependency

Final Recommendation

  • Go with Custom Binary Format if you need absolute top performance and smallest backup size, and don’t need to inspect the backup with external tools.
  • Choose ZipArchive (no compression) if you want a standard format that’s easy to work with, and can tolerate minimal overhead.
  • Pick MessagePack if you want the convenience of a serialization library without rolling your own binary logic.

内容的提问来源于stack exchange,提问作者Oleg Golovkov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:44:35