如何实现按最大尺寸分割结构化数据文件的生成?
最优方案:拆分大文件的DataFileWriter架构修改
核心思路
- 引入流提供者(
Func<Stream>)替代固定单一流,让调用方负责创建新流(如分段文件),DataFileWriter仅处理Stream抽象,满足约束4。 - 提前计算每个
DataElement的大小,结合当前流已写入大小判断是否需要拆分文件,确保每个文件不超过4GB,且元素完整写入,满足约束1、2、3。 - 利用内存流暂存单个元素(因元素不超过4KB,内存开销可忽略),避免直接写入时无法预估大小导致的拆分问题,满足约束5。
修改后的完整代码
public class DataFileWriter : IDisposable { private readonly IEnumerable<DataElement> _input; private readonly Func<Stream> _streamProvider; private Stream _currentStream; private long _currentStreamSize; private const long TOOBIG = 4L * 1024 * 1024 * 1024; // 4 GB public DataFileWriter(IEnumerable<DataElement> input, Func<Stream> streamProvider) { _input = input ?? throw new ArgumentNullException(nameof(input)); _streamProvider = streamProvider ?? throw new ArgumentNullException(nameof(streamProvider)); } public void Write() { // 初始化第一个输出流并写入Header CreateNewStreamAndWriteHeader(); foreach (var element in _input) { // 先将元素序列化到内存流,获取准确大小 using var elementBuffer = new MemoryStream(); WriteElement(element, elementBuffer); elementBuffer.Position = 0; long elementSize = elementBuffer.Length; // 检查当前流剩余空间是否足够容纳该元素 if (_currentStreamSize + elementSize > TOOBIG) { // 关闭当前流,创建新流并写入Header _currentStream.Dispose(); CreateNewStreamAndWriteHeader(); } // 将元素写入当前流 elementBuffer.CopyTo(_currentStream); _currentStreamSize += elementSize; _currentStream.Flush(); } } private void CreateNewStreamAndWriteHeader() { _currentStream = _streamProvider.Invoke(); using var headerBuffer = new MemoryStream(); WriteHeader(headerBuffer); headerBuffer.Position = 0; headerBuffer.CopyTo(_currentStream); _currentStreamSize = headerBuffer.Length; _currentStream.Flush(); } // 原Header序列化逻辑,调整为写入指定Stream private void WriteHeader(Stream stream) { // 此处实现Header的序列化逻辑,例如: // var headerBytes = ...; // stream.Write(headerBytes, 0, headerBytes.Length); } // 原DataElement序列化逻辑,调整为写入指定Stream private void WriteElement(DataElement element, Stream stream) { // 此处实现DataElement的序列化逻辑,例如: // var elementBytes = Serialize(element); // stream.Write(elementBytes, 0, elementBytes.Length); } public void Dispose() { _currentStream?.Dispose(); GC.SuppressFinalize(this); } }
方案优势与约束满足说明
- 约束1(每个文件以Header开头):每次创建新流时都会调用
CreateNewStreamAndWriteHeader,确保每个输出流(文件)都以Header开头。 - 约束2(仅包含完整DataElement):先序列化元素到内存流获取大小,确认当前流能容纳后再写入,绝不会拆分单个元素。
- 约束3(元素仅写入一次):遍历输入集合一次,每个元素仅处理并写入一次。
- 约束4(仅依赖Stream抽象):通过
Func<Stream>委托获取流,DataFileWriter无需知晓流的具体类型(FileStream/NetworkStream等)。 - 约束5(元素大小不固定但≤4KB):用MemoryStream暂存元素,内存占用可控,无性能问题。
使用示例(创建分段文件)
调用方可以通过流提供者创建带序号的文件流,自动生成多个分段文件:
int fileCounter = 1; var dataElements = GetDataElements(); // 获取待写入的DataElement集合 using var writer = new DataFileWriter(dataElements, () => { string fileName = $"data_segment_{fileCounter++}.dat"; return new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); }); writer.Write();
此代码会生成data_segment_1.dat、data_segment_2.dat等文件,每个文件大小不超过4GB,且符合格式要求。
内容的提问来源于stack exchange,提问作者Mason Wheeler
相关产品推荐
相关产品推荐

