【问题标题】:Copy files from one Zip file to another将文件从一个 Zip 文件复制到另一个
【发布时间】:2021-10-07 13:53:36
【问题描述】:

在某些情况下,我将文件从一个 zip 文件复制到另一个文件。我想知道是否有比我想出的更好的方法:

using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read);
using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update);
ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
if (sourceEntry == null) 
    return;
ZipArchiveEntry targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null) 
    targetEntry.Delete();
targetZip.CreateEntry(filePathInArchive);
targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null)
{
    Stream writer = targetEntry.Open();
    Stream reader = sourceEntry.Open();

    int b;
    do
    {
        b = reader.ReadByte();
        writer.WriteByte((byte)b);
    } while (b != -1);


    writer.Close();
    reader.Close();
}

我们将不胜感激提示和建议。

【问题讨论】:

  • 这肯定会损坏文件。你去把你收到的“-1”写到传输的文件上,意思是“流结束”。考虑while(true) .. if(b==-1)break;?
  • 您的代码似乎缺少一些大括号。 using 语句将一直工作到using ZipArchive targetZip,此时唯一受using 语句影响的代码行是ZipArchiveEntry sourceEntry = ... 放入大括号。
  • 我们严格知道目标 zip 存在并且不会创建新的?
  • @RobertHarvey 从 C# 8 开始,不需要大括号,using 的作用域一直到当前作用域的结尾。
  • 你看过Stream.CopyTo而不是手动复制吗?

标签: c# .net zip zipfile system.io.compression


【解决方案1】:

您可以通过打开其流并使用 Stream.CopyTo 将源条目内容写入目标条目来迭代源存档中的每个条目。

来自C# 8.0,它看起来紧凑且工作正常:

static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
    using FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open);
    using FileStream targetFS = new FileStream(targetZipFile, FileMode.Open);

    using ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251));
    using ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251));

    foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
    {
        // 'is' is replacement for 'null' check
        if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
            existingTargetEntry.Delete();

        using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
        {
            sourceEntry.Open().CopyTo(targetEntryStream);
        }
    }
}

C# 8.0 之前的版本也可以正常工作,但需要更多大括号:

static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
    using (FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open))
    {
        using (FileStream targetFS = new FileStream(targetZipFile, FileMode.Open))
        {
            using (ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251)))
            {
                using (ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251)))
                {
                    foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
                    {
                        if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
                        {
                            existingTargetEntry.Delete();
                        }

                        using (Stream target = targetZIP.CreateEntry(sourceEntry.FullName).Open())
                        {
                            sourceEntry.Open().CopyTo(target);
                        }
                    }
                }
            }
        }
    }
}

对于单个指定的文件复制只需将底部部分从foreach循环替换为if条件:

static void CopyZipEntry(string fileName, string sourceZipFile, string targetZipFile)
{
    // ...

    // It means specified file exists in source ZIP-archive
    // and we can copy it to target ZIP-archive
    if (sourceZIP.GetEntry(fileName) is ZipArchiveEntry sourceEntry) 
    {
        if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
            existingTargetEntry.Delete();

        using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
        {
            sourceEntry.Open().CopyTo(targetEntryStream);
        }
    }
    else
        MessageBox.Show("Source ZIP-archive doesn't contains file " + fileName);
}

【讨论】:

    【解决方案2】:

    感谢到目前为止的输入,我清理并改进了代码。我认为这看起来更干净、更可靠。

    //Making sure files exist etc before this part...
    string filePathInArchive = source.GetFilePath(fileId);
    
    using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
    using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
    using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read, false );
    using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update, false);
    
    ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
    
    if (sourceEntry != null)
    {
        if (targetZip.GetEntry(filePathInArchive) is { } existingTargetEntry)
        {
            existingTargetEntry.Delete();
        }
    
        using var targetEntryStream = targetZip.CreateEntry(sourceEntry.FullName).Open();
        sourceEntry.Open().CopyTo(targetEntryStream);
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 2018-10-12
      • 2017-11-16
      相关资源
      最近更新 更多