内存映射对于大文件的使用

平时很少使用大文件的内存映射,碰巧遇到了这样的要求,所以把过程记录下来,当给各位一个引子吧,因为应用不算复杂,可能有考虑不到的地方,欢迎交流。

对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如2G或者更多,文件流就不行了,所以要使用API的内存映射的相关方法,即使是内存映射,也不能一次映射全部文件的大小,所以必须采取分块映射,每次处理一小部分。

先来看几个函数

CreateFile :打开文件

GetFileSize : 获取文件尺寸

CreateFileMapping :创建映射

MapViewOfFile :映射文件

看MapViewOfFile的帮助,他的最后两个参数都需要是页面粒度的整数倍,一般机器的页面粒度为64k(65536字节),而我们实际操作中,一般都不是这样规矩的,任意位置,任意长度都是可能的,所以就要做一些处理。

本例的任务是从一个长度列表中(FInfoList),依次读取长度值,然后到另外一个大文件(FSourceFileName)中去顺序读取指定长度的数据,如果是小文件,这个就好办了,一次读到文件流中,然后依次读取就是了,大数对于大文件,就需要不断改变映射的位置,来取得我们想要的数据。

本例中显示先通过GetSystemInfo来获取页面粒度,然后以10倍的页面粒度为一个映射数据块,在for循环中,会判断已经读取的长度(totallen)加上即将读取的长度,是否在本次映射范围之内(10倍的页面粒度),如果在就继续读取,如果超出了,就要记下剩下的数据,然后重新映射下一块内存,并将记录下的剩余数据合并到新读取的数据中,有点绕啊(可能是我的想法太绕了),下面列出代码。

[delphi] view plaincopy

  1. procedure TGetDataThread.DoGetData;
  2. var
  3. FFile_Handle:THandle;
  4. FFile_Map:THandle;
  5. list:TStringList;
  6. p:PChar;
  7. i,interval:Integer;
  8. begin
  9. try
  10. totallen := 0;
  11. offset := 0;
  12. tstream := TMemoryStream.Create;
  13. stream := TMemoryStream.Create;
  14. list := TStringList.Create;
  15. //获取系统信息
  16. GetSystemInfo(sysinfo);
  17. //页面分配粒度大小
  18. blocksize := sysinfo.dwAllocationGranularity;
  19. //打开文件
  20. FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  21. if FFile_Handle = INVALID_HANDLE_VALUE then Exit;
  22. //获取文件尺寸
  23. filesize := GetFileSize(FFile_Handle,nil);
  24. //创建映射
  25. FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil);
  26. if FFile_Map = 0 then Exit;
  27. //此处我们已10倍blocksize为一个数据块来映射,如果文件尺寸小于10倍blocksize,则直接映射整个文件长度
  28. if filesize div blocksize > 10 then
  29. readlen := 10*blocksize
  30. else
  31. readlen := filesize;
  32. for i := 0 to FInfoList.Count - 1 do
  33. begin
  34. list.Delimiter := ‘:‘;
  35. list.DelimitedText := FInfoList.Strings[i];
  36. //取得长度,我这里做了解析,因为我存储的信息为 a:b:c 这种类型,所以以:号分隔
  37. len := StrToInt(list.Strings[1]);
  38. interval := StrToInt(list.Strings[2]);
  39. if (i = 0) or (totallen+len >=readlen) then
  40. begin
  41. //如果已读取的长度加上即将要读取的长度大于 10倍blocksize,那么我们要保留之前映射末尾的内容,以便和新映射的内容合并
  42. if i > 0 then
  43. begin
  44. offset := offset + readlen;
  45. //写入临时流
  46. tstream.Write(p^,readlen-totallen);
  47. tstream.Position := 0;
  48. end;
  49. //如果未读取的数据长度已经不够一个分配粒度,那么就直接映射剩下的长度
  50. if filesize-offset < blocksize then
  51. readlen := filesize-offset;
  52. //映射,p是指向映射区域的指针
  53. //注意这里第三个参数,一直设为0,这个值要根据实际情况设置
  54. p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,0,offset,readlen));
  55. end;
  56. //如果临时流中有数据,需要合并
  57. if tstream.Size > 0 then
  58. begin
  59. //把临时流数据copy过来
  60. stream.CopyFrom(tstream,tstream.Size);
  61. //然后在末尾写入新数据,合并完成
  62. stream.Write(p^,len-tstream.Size);
  63. totallen := len-tstream.Size;
  64. //移动指针的位置,指向下一个数据的开始
  65. Inc(p,len-tstream.Size);
  66. tstream.Clear;
  67. end
  68. else
  69. begin
  70. stream.Write(p^,len);
  71. totallen := totallen + len;
  72. Inc(p,len);
  73. end;
  74. stream.Position := 0;
  75. //将流保存成文件
  76. stream.SaveToFile(IntToStr(i)+‘.txt‘);
  77. stream.Clear;
  78. end;
  79. finally
  80. stream.Free;
  81. tstream.Free;
  82. CloseHandle(FFile_Handle);
  83. CloseHandle(FFile_Map);
  84. end;
  85. end;

http://blog.csdn.net/bdmh/article/details/6369250

时间: 2024-08-08 22:07:04

内存映射对于大文件的使用的相关文章

Java使用内存映射实现大文件的上传

在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如下为一个对比实验. package test; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOExc

Java中用内存映射处理大文件

在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如下为一个对比实验. [java] view plain copy package test; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileNotFoundExceptio

Java NIO内存映射---上G大文件处理

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了java中内存映射的原理及过程,与传统IO进行了对比,最后,用实例说明了结果. 一.java中的内存映射IO和内存映射文件是什么? 内存映射文件非常特别,它允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快.加载内存映射文件所使用的内存在Java堆区之外.Java编程语言

Java NIO内存映射---上G大文件处理(转)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了java中内存映射的原理及过程,与传统IO进行了对比,最后,用实例说明了结果. 一.java中的内存映射IO和内存映射文件是什么? 内存映射文件非常特别,它允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快.加载内存映射文件所使用的内存在Java堆区之外.Java编程语言

【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射

内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中.注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大:也就是说,它还可以映射一个大文件的某个小片断. MappedByteB

大文件拷贝,试试NIO的内存映射

最近项目里有个需求需要实现文件拷贝,在java中文件拷贝流的读写,很容易就想到IO中的InputStream和OutputStream之类的,但是上网查了一下文件拷贝也是有很多种方法的,除了IO,还有NIO.Apache提供的工具类.JDK自带的文件拷贝方法 IO拷贝 public class IOFileCopy { private static final int BUFFER_SIZE = 1024; public static void copyFile(String source, S

随机访问文件RandomAccessFile 与 内存映射文件MappedByteBuffer

一.RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须是可知的.但是该类仅限于操作文件. RandomAccessFile不属于InputStream和OutputStream类系的.实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系

【JavaNIO的深入研究4】内存映射文件I/O

内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中.注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大:也就是说,它还可以映射一个大文件的某个小片断. MappedByteB

【原创】用JAVA实现大文件上传及显示进度信息

用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 一. 大文件上传基础描述: 各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容. 比如: Spring 框架中使用类似CommonsMultipartFile对象处理表二进制文件信息. 而.NET 中使用HtmlInputFile/ HttpPostedFile对象处理二进制文件信息. 优点:使用框架内置对象可以很方便的