将cab文件解压缩到内存中

时间:2013-07-12 22:23:35

标签: c# asp.net cab

我正在从网络表单上传一个cab文件,并希望将其解压缩到内存中。我尝试使用CabInfo文件解决问题,但没有成功。我知道how to unpack a cab file to my local disk,但不知道如何在内存中应用它。

任何帮助都将不胜感激。

4 个答案:

答案 0 :(得分:1)

如果可以使用其他库,请查看this。该描述清楚地表明该库将允许您提取到memoty。

WIX是一个开源项目。您可以随时询问此功能,在论坛上寻求更好的解决方案,或者只是根据需要修改代码。

瓦迪姆

答案 1 :(得分:1)

您的初始问题似乎表明您可以使用Microsoft.Deployment.Compression DLL。如果为true,则下面的代码可以让您接近您想要的内容:

CabEngine engine = new CabEngine();
foreach (ArchiveFileInfo archiveFileInfo in engine.GetFileInfo(fileStream))
{
    Stream stream = engine.Unpack(fileStream, archiveFileInfo.Name);
    byte[] buffer = new byte[stream.Length];
    //The (int) below is a dirty trick for demonstration purposes only; 
    //re-work for production code
    stream.Read(buffer, 0, (int)stream.Length);
}

您的后续答案似乎表明您不允许使用第三方DLL,在这种情况下您将需要使用FDI * API。此链接包含一些代码,您可以使用硬编码路径修改(内存)流:Extract .cab file in C#

答案 2 :(得分:0)

您可以下载一个随时可用的项目:Cabinet File (*.CAB) Compression and Extraction

根据自2008年9月以来的版本历史"..you can extract directly to memory."

答案 3 :(得分:0)

这是一个应该在内存中工作的实用程序类(它支持x86或x64编译)。如果我们假设使用标准上传协议将.CAB文件上传到ASP.NET,那么你可以使用它:

    using (CabFile file = new CabFile(HttpContext.Current.Request.Files[0].InputStream))
    {
        file.EntryExtract += CabEntryExtract;
        file.ExtractEntries();
    }

    static void CabEntryExtract(object sender, CabEntryExtractEventArgs e)
    {
        // e.Entry.Name contains the entry name
        // e.Entry.Data contains a byte[] with the entry data
        // e.Entry.LastWriteTime contains the entry last write time
        // e.Entry.Size contains the entry uncompressed size
    }

以下是实用程序和相关类:

public sealed class CabFile : IDisposable
{
    private IntPtr _hfdi;
    private ERF _erf;
    private GCHandle _erfHandle;
    private byte[] _data;
    private Dictionary<IntPtr, object> _handles = new Dictionary<IntPtr, object>();
    private MemoryStream _currentEntryData;
    private FNALLOC _alloc;
    private FNCLOSE _close;
    private FNFREE _free;
    private FNOPEN _open;
    private FNREAD _read;
    private FNWRITE _write;
    private FNSEEK _seek;
    private FNFDINOTIFY _extract;

    public event EventHandler<CabEntryExtractEventArgs> EntryExtract;

    public CabFile(string filePath)
        : this(GetStream(filePath))
    {
    }

    private static Stream GetStream(string filePath)
    {
        if (filePath == null)
            throw new ArgumentNullException("filePath");

        return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
    }

    public CabFile(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("stream");

        using (MemoryStream data = new MemoryStream())
        {
            stream.CopyTo(data);
            _data = data.ToArray();
        }
        _erf = new ERF();
        _alloc = new FNALLOC(FnAlloc);
        _free = new FNFREE(FnFree);
        _close = new FNCLOSE(FnClose);
        _open = new FNOPEN(FnOpen);
        _read = new FNREAD(FnRead);
        _write = new FNWRITE(FnWrite);
        _seek = new FNSEEK(FnSeek);
        _extract = new FNFDINOTIFY(FnNotifyExtract);

        _erfHandle = GCHandle.Alloc(_erf, GCHandleType.Pinned);
        _hfdi = FDICreate(
            Marshal.GetFunctionPointerForDelegate(_alloc),
            Marshal.GetFunctionPointerForDelegate(_free),
            Marshal.GetFunctionPointerForDelegate(_open),
            Marshal.GetFunctionPointerForDelegate(_read),
            Marshal.GetFunctionPointerForDelegate(_write),
            Marshal.GetFunctionPointerForDelegate(_close),
            Marshal.GetFunctionPointerForDelegate(_seek)
            , -1, _erfHandle.AddrOfPinnedObject());
    }

    public void ExtractEntries()
    {
        FDICopy(_hfdi, string.Empty, string.Empty, 0, Marshal.GetFunctionPointerForDelegate(_extract), IntPtr.Zero, IntPtr.Zero);
    }

    public void Dispose()
    {
        if (_hfdi != IntPtr.Zero)
        {
            FDIDestroy(_hfdi);
            _hfdi = IntPtr.Zero;
        }

        _erfHandle.Free();
    }

    private void OnEntryExtract(CabEntry entry)
    {
        EventHandler<CabEntryExtractEventArgs> handler = EntryExtract;
        if (handler != null)
        {
            handler(this, new CabEntryExtractEventArgs(entry));
        }
    }

    private IntPtr FnAlloc(int cb)
    {
        return Marshal.AllocHGlobal(cb);
    }

    private void FnFree(IntPtr pv)
    {
        Marshal.FreeHGlobal(pv);
    }

    private IntPtr FnOpen(string pszFile, int oflag, int pmode)
    {
        // only used for reading archive
        IntPtr h = new IntPtr(_handles.Count + 1);
        _handles.Add(h, 0);
        return h;
    }

    private int FnRead(IntPtr hf, byte[] pv, int cb)
    {
        // only used for reading archive
        int pos = (int)_handles[hf];
        int left = _data.Length - pos;
        int read = Math.Min(left, cb);
        if (read > 0)
        {
            Array.Copy(_data, pos, pv, 0, read);
            _handles[hf] = pos + read;
        }
        return read;
    }

    private int FnWrite(IntPtr hf, byte[] pv, int cb)
    {
        // only used for writing entries
        _currentEntryData.Write(pv, 0, cb);
        return cb;
    }

    private int FnClose(IntPtr hf)
    {
        object o = _handles[hf];
        CabEntry entry = o as CabEntry;
        if (entry != null)
        {
            entry.Data = _currentEntryData.ToArray();
            _currentEntryData.Dispose();
        }
        _handles.Remove(hf);
        return 0;
    }

    private int FnSeek(IntPtr hf, int dist, SeekOrigin seektype)
    {
        // only used for seeking archive
        int pos;
        switch (seektype)
        {
            case SeekOrigin.Begin:
                pos = dist;
                break;

            case SeekOrigin.Current:
                pos = (int)_handles[hf] + dist;
                break;

            //case SeekOrigin.End:
            default:
                pos = _data.Length + dist;
                break;
        }
        _handles[hf] = pos;
        return pos;
    }

    private IntPtr FnNotifyExtract(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION fdin)
    {
        CabEntry entry;
        switch (fdint)
        {
            case FDINOTIFICATIONTYPE.COPY_FILE:
                entry = new CabEntry(fdin);
                entry._handle = new IntPtr(_handles.Count + 1);
                _handles.Add(entry._handle, entry);
                _currentEntryData = new MemoryStream();
                return entry._handle;

            case FDINOTIFICATIONTYPE.CLOSE_FILE_INFO:
                entry = (CabEntry)_handles[fdin.hf];
                FnClose(fdin.hf);
                OnEntryExtract(entry);
                return new IntPtr(1);

            default:
                return IntPtr.Zero;
        }
    }

    private enum FDINOTIFICATIONTYPE
    {
        CABINET_INFO = 0,
        PARTIAL_FILE = 1,
        COPY_FILE = 2,
        CLOSE_FILE_INFO = 3,
        NEXT_CABINET = 4,
        ENUMERATE = 5,
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct ERF
    {
        public int erfOper;
        public int erfType;
        public int fError;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    internal class FDINOTIFICATION
    {
        public int cb;
        public IntPtr psz1;
        public IntPtr psz2;
        public IntPtr psz3;
        public IntPtr pv;
        public IntPtr hf;
        public ushort date;
        public ushort time;
        public ushort attribs;
        public ushort setID;
        public ushort iCabinet;
        public ushort iFolder;
        public int fdie;
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate IntPtr FNALLOC(int cb);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate void FNFREE(IntPtr pv);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    private delegate IntPtr FNOPEN(string pszFile, int oflag, int pmode);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate int FNREAD(IntPtr hf, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate int FNWRITE(IntPtr hf, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate int FNCLOSE(IntPtr hf);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate int FNSEEK(IntPtr hf, int dist, SeekOrigin seektype);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate IntPtr FNFDINOTIFY(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION pfdin);

    [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    private static extern IntPtr FDICreate(IntPtr pfnalloc, IntPtr pfnfree, IntPtr pfnopen, IntPtr pfnread, IntPtr pfnwriter, IntPtr pfnclose, IntPtr pfnseek, int cpuType, IntPtr perf);

    [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr FDIDestroy(IntPtr hdfi);

    [DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    private static extern IntPtr FDICopy(IntPtr hdfi, string pszCabinet, string pszCabPath, int flags, IntPtr fnNotify, IntPtr fnDecrypt, IntPtr userData);
}

public sealed class CabEntry
{
    internal IntPtr _handle;

    internal CabEntry(CabFile.FDINOTIFICATION fdin)
    {
        Name = Marshal.PtrToStringAnsi(fdin.psz1);
        Size = fdin.cb;
        LastWriteTime = new DateTime(1980 + GetMask(fdin.date, 9, 15), GetMask(fdin.date, 5, 8), GetMask(fdin.date, 0, 4),
            GetMask(fdin.time, 11, 15), GetMask(fdin.time, 5, 10), 2 * GetMask(fdin.time, 0, 4));
    }

    private static int GetMask(int value, byte startByte, byte endByte)
    {
        int final = 0;
        int v = 1;
        for (byte b = startByte; b <= endByte; b++)
        {
            if ((value & (1 << b)) != 0)
            {
                final += v;
            }
            v = v * 2;
        }
        return final;
    }

    public string Name { get; private set; }
    public int Size { get; private set; }
    public DateTime LastWriteTime { get; private set; }
    public byte[] Data { get; internal set; }
}

public sealed class CabEntryExtractEventArgs : EventArgs
{
    public CabEntryExtractEventArgs(CabEntry entry)
    {
        if (entry == null)
            throw new ArgumentNullException("entry");

        Entry = entry;
    }

    public CabEntry Entry { get; private set; }
}

注意:此代码分配了大字节[]块,因此可以对其进行优化,以使用ChunkedMemoryStream(例如在此库中可用:CodeFluent Runtime Client)而不是byte []来避免影响LOH({{ 3}}}太多了。