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

如何使用SharpZipLib向现有TAR文件添加条目?

向现有TAR文件添加条目的正确方式

问题原因

TAR文件有固定结构规则:

  • 每个文件条目由512字节的头部 + 按512字节对齐的文件数据组成
  • 文件末尾必须以两个连续的512字节空块作为结束标记

你的现有代码存在两个核心问题:

  • 使用FileMode.Open时,默认从文件开头写入,直接覆盖原有内容
  • 使用FileMode.Append时,新条目被写入到结束标记之后,TAR读取工具只会识别结束标记前的内容,后续数据被判定为"额外数据"无法识别

解决方案

修改TarFileWriter类,处理现有TAR文件的结束标记,将新条目插入到结束标记之前,写入完成后重新生成合法的结束标记。

修改后的TarFileWriter类

namespace MyNamespace
{
    using System;
    using System.IO;
    using ICSharpCode.SharpZipLib.Tar;

    public class TarFileWriter : IFileWriter
    {
        private const int TarBlockSize = 512;
        private Stream FileStream { get; }
        private TarOutputStream OutputStream { get; }
        private bool _isAppending;

        public TarFileWriter(string filename, FileMode mode)
        {
            FileStream = File.Open(filename, mode);
            _isAppending = mode != FileMode.CreateNew && mode != FileMode.Create;

            if (_isAppending)
            {
                // 定位到文件末尾,检查并跳过TAR的结束标记(两个512字节空块)
                if (FileStream.Length >= 2 * TarBlockSize)
                {
                    FileStream.Seek(-2 * TarBlockSize, SeekOrigin.End);
                    var buffer = new byte[TarBlockSize];
                    // 验证第一个结束块是否为空
                    int read = FileStream.Read(buffer, 0, TarBlockSize);
                    if (read == TarBlockSize && Array.TrueForAll(buffer, b => b == 0))
                    {
                        // 验证第二个结束块是否为空
                        read = FileStream.Read(buffer, 0, TarBlockSize);
                        if (read == TarBlockSize && Array.TrueForAll(buffer, b => b == 0))
                        {
                            // 两个空块都存在,将指针移到结束块之前,准备写入新条目
                            FileStream.Seek(-2 * TarBlockSize, SeekOrigin.End);
                        }
                        else
                        {
                            // 不存在完整结束块,回到文件末尾
                            FileStream.Seek(0, SeekOrigin.End);
                        }
                    }
                    else
                    {
                        // 第一个空块不存在,回到文件末尾
                        FileStream.Seek(0, SeekOrigin.End);
                    }
                }
            }

            OutputStream = new TarOutputStream(FileStream, null);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // 追加模式下,确保写入合法的TAR结束标记
                if (_isAppending)
                {
                    OutputStream.Finish();
                }
                OutputStream.Dispose();
                FileStream.Dispose();
            }
        }

        public void WriteAllBytes(string filename, byte[] bytes)
        {
            var tarHeader = new TarHeader
            {
                Name = filename,
                ModTime = DateTime.Now,
            };
            var entry = new TarEntry(tarHeader)
            {
                Size = bytes.Length,
            };

            OutputStream.PutNextEntry(entry);
            OutputStream.Write(bytes, 0, bytes.Length);
            OutputStream.CloseEntry();
        }
    }

    // 补充IFileWriter接口定义(若项目中未定义)
    public interface IFileWriter : IDisposable
    {
        void WriteAllBytes(string filename, byte[] bytes);
    }
}

正确的测试调用方式

[TestClass]
public class FileToolTest
{
    [TestMethod]
    public void CreateTarFile()
    {
        using (var tarFileWriter = new TarFileWriter(@"H:\TestTar.tar", FileMode.CreateNew))
        {
            tarFileWriter.WriteAllBytes("Test.data", new byte[] { 12, 34, 56 });
        }
    }

    [TestMethod]
    public void AddEntriesToExistingTar()
    {
        // 使用FileMode.Open即可,构造函数会自动处理追加逻辑
        using (var tarFileWriter = new TarFileWriter(@"H:\TestTar.tar", FileMode.Open))
        {
            tarFileWriter.WriteAllBytes("Test1.data", new byte[] { 12, 34, 56 });
            tarFileWriter.WriteAllBytes("Test2.data", new byte[] { 78, 90, 12 });
        }
    }
}

关键说明

  1. 结束标记处理:打开现有TAR文件时,先检查末尾的两个空块,将文件指针移动到结束标记之前,确保新条目写入到合法位置
  2. 结束标记重写:追加完成后调用OutputStream.Finish(),自动写入两个空块作为新的结束标记,保证TAR文件结构合法
  3. 模式选择:追加时使用FileMode.Open即可,无需使用FileMode.Append,构造函数已处理所有追加逻辑

内容的提问来源于stack exchange,提问作者Peter Buchmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:44:55