1

DotNetZipライブラリを使用して、zipアーカイブ内のファイルの名前を変更しています。

これが私が使用しているコードです:

void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        ZipEntry targetFile = zip.Entries.FirstOrDefault(f =>
            f.FileName.ToLower() == oldName.ToLower());

        //targetFile is not null.

        targetFile.FileName = newName;
        zip.Save();
    }
}

私が抱えている問題は、名前の変更操作の後、zipが破損していることです。WinRARを使用して参照すると、名前を変更したファイルのエントリが重複しています(両方のエントリは同じで、新しい名前になっています)。それを抽出しようとすると、WinRARは2つの同一のエントリに対してCRCエラーをスローします。

別のファイル名で保存しようとしましたが、うまくいきませんでした。

ライブラリのv1.9.1.8(縮小版)を使用しています。

アップデート:

名前の変更後に重複するエントリの1つを削除すると機能するようで、目に見える副作用はありません。それでも、ハッキーでないアプローチは歓迎されます。

void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        ZipEntry targetFile = zip.Entries.FirstOrDefault(f =>
            f.FileName.ToLower() == oldName.ToLower());

        //targetFile is not null.

        targetFile.FileName = newName;

        //if the file was duplicated, remove one of the duplicates:
        var dupFiles = zip.Where(f => f.FileName == newName).ToList();
        if (dupFiles.Count > 1)
        {
            zip.RemoveEntries(dupFiles.Skip(1).ToList());
        }

        zip.Save();
    }
}

アップデート2:

回答がソースファイルに問題がある可能性があることを示唆しているので、サンプルのzipファイル(24kb)をここにアップロードしました

これは、この問題が発生していることを確認するために使用しているコードです。

using (ZipFile zip = ZipFile.Read("test.zip"))
{
    ZipEntry entry = zip.Entries.FirstOrDefault(e => Path.GetFileName(e.FileName) == "test.max");
    entry.FileName = Path.Combine(Path.GetDirectoryName(entry.FileName), "newname.max");
    zip.Save(@"test2.zip");
}
4

2 に答える 2

2

私が横になっていたランダムなZIPファイルと、最新の安定した1.9.1.8Reducedプリコンパイル済みバイナリまたは最新のReducedソースコードで問題を再現することはできません。投稿したコードはZIPアーカイブになり、Windowsエクスプローラーと7zipの両方を使用してファイルを正常に開いて抽出できます(WinRARはありません)。

Anders Gustafssonのアプローチアプローチはどちらも同じZipFile内部データ構造を使用しているため、違いはなく、コードは正常に機能するはずです。

public ZipEntry this[String fileName]
{
    get
    {
        if (_entries.ContainsKey(fileName))
            return _entries[fileName];
        return null;
    }
}

public ICollection<ZipEntry> Entries
{
    get { return _entries.Values; }
}

使用しているソースZIPファイル、そのZIPのフルパス(長すぎる?)、または選択した新旧のファイル名に問題がある可能性があります。または、DotNetZipが古いエントリを削除できないようにする別のアプリケーションを開いている可能性がありますか?


更新:問題が見つかりました!

この問題は確かにZIPファイルに固有のものでしたが、実際にはDotNetZipのバグです。

ライブラリがZIPファイルからファイルエントリを読み取るとき、ZIPファイルで指定されたとおりにファイル名を取得し、のFilenameプロパティに保存してZipEntryから、このファイル名をキーとして_entries辞書に保存します。この場合、Filenameファイルエントリのプロパティの値は次のとおりです。

C:\ Users \ rotem \ Documents \ 3dsmax \scenes \ test.max

次に、Filenameプロパティの値を変更すると、DotNetZipは現在のエントリをから削除_entriesし、新しいファイル名でエントリを再度追加します。

現在のエントリを削除するには、現在のファイル名を正規化し、そのファイル名のエントリをから削除します_entries。したがって、この正規化されたファイル名をキーとしてエントリを削除しようとしています。

Users / rotem / Documents / 3dsmax / sites / test.max

もちろん、_entriesそのファイル名にはキーがありません。削除する予定のエントリには、C:\プレフィックスとすべての円記号があります。したがって、古いエントリは削除されません。

しかし、DotNetZipはこれに気付かず、続行します。エントリFilenameを正規化された新しいファイル名に設定します。

Users / rotem / Documents / 3dsmax / sites / newname.max

_entries次に、エントリを辞書に再度追加します。これで、同じファイルエントリに対して2つのエントリがあります。1つは正規化された新しいファイル名で、もう1つは正規化されていない古いファイル名です。そして、そこからすべての問題が発生します。

DotNetZipは、名前の変更時に実際に1つのエントリを削除したと主張することをお勧めします。また、プロパティまたはそのバッキングフィールドに割り当てられているファイル名、またはのキーとして使用されているファイル名を正規化する必要があります。ZipEntry.Filename_entries


問題#16130は、DotNetZipCodePlexページで作成されました。

于 2013-03-27T18:44:45.007 に答える
0

あなたのアプローチは合理的であるように思われるので、 DotNetZipライブラリにバグがある可能性があります。

ただし、DotNetZip CodePlexサイトの「zipアーカイブの更新」セクションのC#の例を見るアーカイブの既存のエントリの名前を変更するための推奨(?)アプローチは次のとおりです。

zip[oldName].FileName = newName;

これは、メソッドが次のように変更されることを意味します。

void RenameFile(string existingArchive, string oldName, string newName)
{
    using (ZipFile zip = ZipFile.Read(existingArchive))
    {
        zip[oldName].FileName = newName;
        zip.Save();
    }
}

テストからわかる限り、上記のアプローチは意図したとおりに機能し、インデクサープロパティthis[string fileName]は大文字と小文字を区別しないファイル名を一致させるように見えます

于 2013-03-27T13:00:30.267 に答える