【问题标题】:Unpack cab file in memory解压内存中的cab文件
【发布时间】:2013-07-11 14:05:46
【问题描述】:

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

任何帮助将不胜感激。

【问题讨论】:

  • Adam_G - 对您有帮助吗?

标签: c# asp.net cab


【解决方案1】:

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

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

瓦迪姆

【讨论】:

  • 谢谢瓦迪姆。不幸的是,我的公司不允许我使用 3rd-party 库。
  • 不过,我可以使用 p/invoke,这将允许我使用 c++ API FDICopy() (msdn.microsoft.com/en-us/library/bb432270(v=vs.85).aspx)。你知道我将如何使用这段代码来实现吗?
  • 看一下我建议使用的项目的源码。特别是在 Extract.hpp。有一个名为 ExtractFileW 的函数,如果 msw_TargetDir 为“MEMORY”,则 mb_ExtractToMemory 设置为 true。这反过来会影响 FdiWrite 的工作方式。当您创建 FDIContext 时,您提供自己的写入处理程序,该处理程序获取数据,如果 mb_ExtractToMemory 为真,它将将其写入内存。但是,您似乎无法使用单个 PINVOKE 执行所有这些操作。我会根据上述代码创建自己的 C++ dll 并对其进行 PINVOKE。
  • 魔法真的发生在 FCICreate 和 FNFCIWRITE 上。据我所知,您必须编写自己的 FNFCIWRITE(或重用项目中的部分),调用 FCICreate,然后才调用 FDICOPY。
  • 谢谢瓦迪姆。我暂时不得不跳过项目的这一部分,但很快就会回复你。
【解决方案2】:

您最初的问题似乎表明您可以使用 Microsoft.Deployment.Compression DLL。如果为真,下面的代码应该能让你接近你想要的:

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);
}

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

【讨论】:

    【解决方案3】:

    有一个现成的项目可以下载:Cabinet File (*.CAB) Compression and Extraction

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

    【讨论】:

      【解决方案4】:

      这是一个应该在内存中工作的实用程序类(它支持 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)而不是字节 [],以避免影响 LOH (Large Object Heap)太多了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-07
        • 2013-07-22
        • 1970-01-01
        • 1970-01-01
        • 2013-02-27
        • 2015-11-16
        相关资源
        最近更新 更多