天天看點

用記憶體映射檔案的方式讀取大檔案

           MongoDB使用記憶體映射檔案的方式來實作對資料庫檔案的快速操作.在實際的工作中,也經常用到記憶體映射檔案的方式來讀取大檔案.

           記憶體映射檔案是磁盤檔案的全部或部分内容與虛拟位址空間的某個區域建立關聯,可以對被映射的檔案進行直接通路,而不必執行檔案I/O操作也無需對檔案内容進行緩存處理.

           下面就是c#使用記憶體映射檔案方式讀取檔案的代碼.

[StructLayout(LayoutKind.Sequential)]
        //系統資訊結構
        internal struct SYSTEM_INFO
        {
            public uint dwOemId;
            public uint dwPageSize;
            public uint lpMinimumApplicationAddress;
            public uint lpMaximumApplicationAddress;
            public uint dwActiveProcessorMask;
            public uint dwNumberOfProcessors;
            public uint dwProcessorType;
            public uint dwAllocationGranularity;
            public uint dwProcessorLevel;
            public uint dwProcessorRevision;
        }

        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;
        private const int OPEN_EXISTING = 3;
        private const int INVALID_HANDLE_VALUE = -1;
        private const int FILE_FLAG_NO_BUFFERING = 0x20000000;
        private const int FILE_ATTRIBUTE_NORMAL = 0x80;
        private const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
        private const uint PAGE_READWRITE = 0x04;
        private const uint PAGE_READONLY = 0x02;

        private const int FILE_MAP_COPY = 1;
        private const int FILE_MAP_WRITE = 2;
        private const int FILE_MAP_READ = 4;


        // 記憶體映射檔案句柄
        [DllImport("kernel32.dll")]
        internal static extern IntPtr CreateFileMapping(IntPtr hFile,
            IntPtr lpFileMappingAttributes, uint flProtect,
            uint dwMaximumSizeHigh,
            uint dwMaximumSizeLow, string lpName);

        // 記憶體映射檔案
        [DllImport("kernel32.dll")]
        internal static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
            dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
            uint dwNumberOfBytesToMap);

        // 撤消檔案映像
        [DllImport("kernel32.dll")]
        internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

        // 關閉核心對象句柄
        [DllImport("kernel32.dll")]
        internal static extern bool CloseHandle(IntPtr hObject);

        // 打開要映射的檔案
        [DllImport("kernel32.dll")]
        internal static extern IntPtr CreateFile(string lpFileName,
            uint dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs,
            FileMode dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        // 得到檔案大小
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern uint GetFileSize(IntPtr hFile, out uint highSize);

        //得到系統資訊
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo);

        public bool ReadBigFile(string strBigFile)
        {
            //建立/打開一個檔案核心對象
            IntPtr fileHandle = CreateFile(strBigFile,
        GENERIC_READ, FileShare.Read,
        IntPtr.Zero, FileMode.Open,
        FILE_FLAG_NO_BUFFERING, IntPtr.Zero);

            if (INVALID_HANDLE_VALUE != (int)fileHandle)
            {
                //建立一個檔案映射核心對象
                IntPtr mappingFileHandle = CreateFileMapping(
                    fileHandle, IntPtr.Zero, PAGE_READONLY, 0, 0, "~MappingTemp");
                if (mappingFileHandle != IntPtr.Zero)
                {
                    SYSTEM_INFO systemInfo = new SYSTEM_INFO(); ;
                    GetSystemInfo(ref systemInfo);
                    //得到系統頁配置設定粒度
                    uint allocationGranularity = systemInfo.dwAllocationGranularity;
                    uint fileSizeHigh = 0;
                    uint fileSize = GetFileSize(fileHandle, out fileSizeHigh);
                    fileSize |= (((uint)fileSizeHigh) << 32);
                    //關閉檔案句柄 
                    CloseHandle(fileHandle);
                    uint fileOffset = 0;
                    uint blockBytes = 1000 * allocationGranularity;
                    if (fileSize < 1000 * allocationGranularity)
                        blockBytes = fileSize;

                    //分塊讀取記憶體
                    while (fileSize > 0)
                    {
                        if (fileSize < 1000 * allocationGranularity)
                            blockBytes = fileSize;
                        // 映射視圖,得到位址 
                        IntPtr lpbMapAddress = MapViewOfFile(mappingFileHandle, FILE_MAP_READ,
                           0, (uint)(fileOffset & 0xFFFFFFFF),
                           blockBytes);

                        if (lpbMapAddress == IntPtr.Zero)
                        {
                            return false;
                        }
                        // 對映射的視圖進行通路
                        byte[] temp = new byte[blockBytes];
                        //從非托管的記憶體中複制内容到托管的記憶體中
                        Marshal.Copy(lpbMapAddress, temp, 0, (int)blockBytes);

                        //---------do something----------------------

                        // 撤消檔案映像
                        UnmapViewOfFile(lpbMapAddress);
                        // 修正參數
                        fileOffset += blockBytes;
                        fileSize -= blockBytes;
                    }
                    //關閉檔案映射
                    CloseHandle(mappingFileHandle);
                }

            }
            return true;
        }
           

繼續閱讀