web-dev-qa-db-ja.com

C#.netはZipファイルを識別します

現在、SharpZipAPIを使用してZipファイルエントリを処理しています。圧縮と解凍に最適です。ただし、ファイルがZipであるかどうかを識別するのに問題があります。ファイルストリームを解凍できるかどうかを検出する方法があるかどうかを知る必要があります。もともと使っていた

FileStream lFileStreamIn = File.OpenRead(mSourceFile);
lZipFile = new ZipFile(lFileStreamIn);
ZipInputStream lZipStreamTester = new ZipInputStream(lFileStreamIn, mBufferSize);// not working
lZipStreamTester.Read(lBuffer, 0, 0);
if (lZipStreamTester.CanDecompressEntry)
{

LZipStreamTesterは毎回nullになり、ifステートメントは失敗します。バッファあり/なしで試してみました。誰かがその理由について何か洞察を与えることができますか?ファイル拡張子を確認できることは承知しています。それよりも決定的なものが必要です。 Zipには魔法の#(PK何か)があることも知っていますが、フォーマットの要件ではないため、常にそこにあるという保証はありません。

また、ネイティブZipをサポートする.net 4.5について読んだので、プロジェクトはsharpzipではなくそれに移行する可能性がありますが、CanDecompressEntryに似たメソッド/パラメーターはここに表示されませんでした: http://msdn.Microsoft。 com/en-us/library/3z72378a%28v = vs.110%29

私の最後の手段は、try catchを使用して、ファイルの解凍を試みることです。

16
Sean Dunford

これは、非圧縮、PKZIP圧縮(sharpziplib)、またはGZip圧縮(.netに組み込まれている)のいずれかのデータを処理する必要があるコンポーネントの基本クラスです。おそらくあなたが必要とするより少し多いですが、あなたを動かすはずです。これは、@ PhonicUKの提案を使用してデータストリームのヘッダーを解析する例です。小さなファクトリmathodに表示される派生クラスは、PKZipおよびGZip解凍の詳細を処理しました。

abstract class Expander
{
    private const int Zip_LEAD_BYTES = 0x04034b50;
    private const ushort GZIP_LEAD_BYTES = 0x8b1f;

    public abstract MemoryStream Expand(Stream stream); 

    internal static bool IsPkZipCompressedData(byte[] data)
    {
        Debug.Assert(data != null && data.Length >= 4);
        // if the first 4 bytes of the array are the Zip signature then it is compressed data
        return (BitConverter.ToInt32(data, 0) == Zip_LEAD_BYTES);
    }

    internal static bool IsGZipCompressedData(byte[] data)
    {
        Debug.Assert(data != null && data.Length >= 2);
        // if the first 2 bytes of the array are theG Zip signature then it is compressed data;
        return (BitConverter.ToUInt16(data, 0) == GZIP_LEAD_BYTES);
    }

    public static bool IsCompressedData(byte[] data)
    {
        return IsPkZipCompressedData(data) || IsGZipCompressedData(data);
    }

    public static Expander GetExpander(Stream stream)
    {
        Debug.Assert(stream != null);
        Debug.Assert(stream.CanSeek);
        stream.Seek(0, 0);

        try
        {
            byte[] bytes = new byte[4];

            stream.Read(bytes, 0, 4);

            if (IsGZipCompressedData(bytes))
                return new GZipExpander();

            if (IsPkZipCompressedData(bytes))
                return new ZipExpander();

            return new NullExpander();
        }
        finally
        {
            stream.Seek(0, 0);  // set the stream back to the begining
        }
    }
}
13
dkackman

表示 https://stackoverflow.com/a/16587134/2067 リファレンス

以下のリンクを確認してください。

icsharpcode-sharpziplib-validate-Zip-file

How-to-check-if-a-file-is-compressed-in-c#

Zipファイルは常に0x04034b50(4バイト)で始まります
もっと見る: http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers

使用例:

        bool isPKZip = IOHelper.CheckSignature(pkg, 4, IOHelper.SignatureZip);
        Assert.IsTrue(isPKZip, "Not Zip the package : " + pkg);

// http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/
    public static partial class IOHelper
    {
        public const string SignatureGzip = "1F-8B-08";
        public const string SignatureZip = "50-4B-03-04";

        public static bool CheckSignature(string filepath, int signatureSize, string expectedSignature)
        {
            if (String.IsNullOrEmpty(filepath)) throw new ArgumentException("Must specify a filepath");
            if (String.IsNullOrEmpty(expectedSignature)) throw new ArgumentException("Must specify a value for the expected file signature");
            using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                if (fs.Length < signatureSize)
                    return false;
                byte[] signature = new byte[signatureSize];
                int bytesRequired = signatureSize;
                int index = 0;
                while (bytesRequired > 0)
                {
                    int bytesRead = fs.Read(signature, index, bytesRequired);
                    bytesRequired -= bytesRead;
                    index += bytesRead;
                }
                string actualSignature = BitConverter.ToString(signature);
                if (actualSignature == expectedSignature) return true;
                return false;
            }
        }

    }
12
Kiquenet

次のいずれかを実行できます。

  • Try-catch構造を使用して、潜在的なZipファイルの構造を読み取ってみてください
  • ファイルヘッダーを解析して、Zipファイルかどうかを確認します

Zipファイルは常に最初の4バイトが0x04034b50で始まります( http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers

8
PhonicUK

Web用にプログラミングしている場合は、ファイルContent Type:application/Zipを確認できます。

2
Akira Yamamoto

上記の回答を提供してくれたdkackmanとKiquenetに感謝します。完全を期すために、以下のコードは署名を使用して圧縮(Zip)ファイルを識別します。次に、新しいMS Officeファイル形式もこの署名ルックアップ(.docxファイルや.xlsxファイルなど)と一致するように返される複雑さが増します。他の場所で述べたように、これらは確かに圧縮されたアーカイブです。ファイルの名前を.Zip拡張子に変更して、内部のXMLを確認できます。

以下のコードでは、最初に上記で使用した署名を使用してZip(圧縮)のチェックを行い、次にMSOfficeパッケージのチェックを行います。 System.IO.Packaging.Packageを使用するには、「WindowsBase」へのプロジェクト参照(つまり、.NETアセンブリ参照)が必要であることに注意してください。

    private const string SignatureZip = "50-4B-03-04";
    private const string SignatureGzip = "1F-8B-08";

    public static bool IsZip(this Stream stream)
    {
        if (stream.Position > 0)
        {
            stream.Seek(0, SeekOrigin.Begin);
        }

        bool isZip = CheckSignature(stream, 4, SignatureZip);
        bool isGzip = CheckSignature(stream, 3, SignatureGzip);

        bool isSomeKindOfZip = isZip || isGzip;

        if (isSomeKindOfZip && stream.IsPackage()) //Signature matches Zip, but it's package format (docx etc).
        {
            return false;
        }

        return isSomeKindOfZip;
    }

    /// <summary>
    /// MS .docx, .xslx and other extensions are (correctly) identified as Zip files using signature lookup.
    /// This tests if System.IO.Packaging is able to open, and if package has parts, this is not a Zip file.
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    private static bool IsPackage(this Stream stream)
    {
        Package package = Package.Open(stream, FileMode.Open, FileAccess.Read);
        return package.GetParts().Any();
    }
1
user369142

https://en.wikipedia.org/wiki/List_of_file_signatures を使用して、ZipファイルとWordドキュメントを区別するためにZipファイルに1バイトを追加しました(これらは最初の4バイトを共有します) )。

これが私のコードです:

public class ZipFileUtilities
{
    private static readonly byte[] ZipBytes1 = { 0x50, 0x4b, 0x03, 0x04, 0x0a };
    private static readonly byte[] GzipBytes = { 0x1f, 0x8b };
    private static readonly byte[] TarBytes = { 0x1f, 0x9d };
    private static readonly byte[] LzhBytes = { 0x1f, 0xa0 };
    private static readonly byte[] Bzip2Bytes = { 0x42, 0x5a, 0x68 };
    private static readonly byte[] LzipBytes = { 0x4c, 0x5a, 0x49, 0x50 };
    private static readonly byte[] ZipBytes2 = { 0x50, 0x4b, 0x05, 0x06 };
    private static readonly byte[] ZipBytes3 = { 0x50, 0x4b, 0x07, 0x08 };

    public static byte[] GetFirstBytes(string filepath, int length)
    {
        using (var sr = new StreamReader(filepath))
        {
            sr.BaseStream.Seek(0, 0);
            var bytes = new byte[length];
            sr.BaseStream.Read(bytes, 0, length);

            return bytes;
        }
    }

    public static bool IsZipFile(string filepath)
    {
        return IsCompressedData(GetFirstBytes(filepath, 5));
    }

    public static bool IsCompressedData(byte[] data)
    {
        foreach (var headerBytes in new[] { ZipBytes1, ZipBytes2, ZipBytes3, GzipBytes, TarBytes, LzhBytes, Bzip2Bytes, LzipBytes })
        {
            if (HeaderBytesMatch(headerBytes, data))
                return true;
        }

        return false;
    }

    private static bool HeaderBytesMatch(byte[] headerBytes, byte[] dataBytes)
    {
        if (dataBytes.Length < headerBytes.Length)
            throw new ArgumentOutOfRangeException(nameof(dataBytes), 
                $"Passed databytes length ({dataBytes.Length}) is shorter than the headerbytes ({headerBytes.Length})");

        for (var i = 0; i < headerBytes.Length; i++)
        {
            if (headerBytes[i] == dataBytes[i]) continue;

            return false;
        }

        return true;
    }

 }

これをコーディングするより良い方法、特にバイト比較があるかもしれませんが、可変長のバイト比較(チェックされている署名に応じて)として、少なくともこのコードは読み取り可能であると感じました-少なくとも私には。

0
Philip Johnson