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

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

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

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

先来看几个函数

CreateFile :打开文件

GetFileSize : 获取文件尺寸

CreateFileMapping :创建映射

MapViewOfFile :映射文件

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

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

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

下面列出代码。

procedure TGetDataThread.DoGetData;
var
 FFile_Handle:THandle;
 FFile_Map:THandle;
 list:TStringList;
 p:PChar;
 i,interval:Integer;
begin
 try
 totallen := 0;
 offset := 0;
 tstream := TMemoryStream.Create;
 stream := TMemoryStream.Create;
 list := TStringList.Create;
 //获取系统信息
 GetSystemInfo(sysinfo);
 //页面分配粒度大小
 blocksize := sysinfo.dwAllocationGranularity;
 //打开文件
 FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
 if FFile_Handle = INVALID_HANDLE_VALUE then Exit;
 //获取文件尺寸
 filesize := GetFileSize(FFile_Handle,nil);
 //创建映射
 FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil);
 if FFile_Map = 0 then Exit;
 //此处我们已10倍blocksize为一个数据块来映射,如果文件尺寸小于10倍blocksize,则直接映射整个文件长度
 if filesize div blocksize > 10 then
  readlen := 10*blocksize
 else
  readlen := filesize;
 for i := 0 to FInfoList.Count - 1 do
 begin
  list.Delimiter := ':';
  list.DelimitedText := FInfoList.Strings[i];
  //取得长度,我这里做了解析,因为我存储的信息为 a:b:c 这种类型,所以以:号分隔
  len := StrToInt(list.Strings[1]);
  interval := StrToInt(list.Strings[2]);
  if (i = 0) or (totallen+len >=readlen) then
  begin
   //如果已读取的长度加上即将要读取的长度大于 10倍blocksize,那么我们要保留之前映射末尾的内容,以便和新映射的内容合并
   if i > 0 then
   begin
    offset := offset + readlen;
    //写入临时流
    tstream.Write(p^,readlen-totallen);
    tstream.Position := 0;
   end;
   //如果未读取的数据长度已经不够一个分配粒度,那么就直接映射剩下的长度
   if filesize-offset < blocksize then
    readlen := filesize-offset;
   //映射,p是指向映射区域的指针
   //注意这里第三个参数,一直设为0,这个值要根据实际情况设置
   p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,0,offset,readlen));
  end;
  //如果临时流中有数据,需要合并
  if tstream.Size > 0 then
  begin
   //把临时流数据copy过来
   stream.CopyFrom(tstream,tstream.Size);
   //然后在末尾写入新数据,合并完成
   stream.Write(p^,len-tstream.Size);
   totallen := len-tstream.Size;
   //移动指针的位置,指向下一个数据的开始
   Inc(p,len-tstream.Size);
   tstream.Clear;
  end
  else
  begin
   stream.Write(p^,len);
   totallen := totallen + len;
   Inc(p,len);
  end;
  stream.Position := 0;
  //将流保存成文件
  stream.SaveToFile(IntToStr(i)+'.txt');
  stream.Clear;
 end;
 finally
  stream.Free;
  tstream.Free;
  CloseHandle(FFile_Handle);
  CloseHandle(FFile_Map);
 end;
end;

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Delphi 实现软件自动升级的功能

    Delphi 实现软件自动升级的功能 原理简单,在FTP上维护一个Update.ini文件,里面记录着要更新文件的版本号,本地也有一个Update.ini文件,每次启动更新程序时,先从FTP上下载Update.ini文件到本地名字为Update_new.ini,然后比较这两个文件,如果新的版本号大于旧的,或者新的文件在就ini中没有,这些就表示要更新的文件,然后逐一下载. 本程序名字为AutoUpdate,你生成这个exe,然后和主程序一起打包,创建桌面快捷方式时,指向AutoUpdate,而不

  • Delphi 根据字符串找到函数并执行的实例

    Delphi 根据字符串找到函数并执行的实例  关键字:MethodAddress:取得方法的地址,这个方法需要是published的. 实例代码: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TShowInfo = procedure(info:string) of object; //声

  • Delphi XE5 为Android应用制作签名的方法(图文)

    要发布android应用,必须做签名的.看一下具体的操作步骤: 1.进入Project-Options: 2.打开Options窗口,选择Provisioning,在这里,可以为目标平台选择签名文件,也可生成新的签名文件. 2.1 选择目标平台,可以为dubug或release设置签名,默认情况下,debug签名文件是存在的,可不用设置,这也是我们建立一个新的Android项目,就可以不用签名就能在模拟器上跑的原因,真正发布时,是必须做签名的. 2.2,为发布做一个新的签名,选择Target为R

  • Delphi 用DLL实现插件的简单实例

    Delphi 用DLL实现插件的简单实例 这是DLL的代码 实现代码: library MyDll; uses SysUtils, Dialogs, Classes; procedure ShowInfo(info:PChar);stdcall; begin ShowMessage('您选择了['+info+']'); end; function GetCaption:Pchar; begin Result := '中国'; end; exports ShowInfo, GetCaption;

  • Delphi实现窗体感知鼠标滑过并自动隐藏与显示窗口的方法

    本文实例讲述了Delphi实现窗体感知鼠标滑过并自动隐藏与显示窗口的方法.分享给大家供大家参考.具体实现方法如下: const WM_MouseEnter = $B013; WM_MouseLeave = $B014; type TfrmMain = class(TForm) . . Timer1: TTimer; procedure Timer1Timer(Sender: TObject); protected procedure WMMouseEnter(var Msg: TMessage)

  • ListView 百分比进度条(delphi版)

    在看代码之前先给大家附上效果图: 废话不多说了,直接给大家贴代码了. unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ImgList; type TForm1 = class(TForm) btn1: TButton; lv1: TListView; trckbr1: TTrackBa

  • Delphi提取PDF文本实例

    生成PDF的控件很多,但解析的不是太多,pdf Toolkit可以,但测试的第一个复杂的pdf就报告错误,并且汉字乱码,可能使用的版本或使用方法不对. 想起之前使用java调用的Apache名下的pdfBox库很好用,于是就用下载了pdfBox,使用Delphi来调用pdfBox解析pdf文本. 环境要求:java运行环境 pdfBox应用包:pdfbox-app-2.0.6.jar 这里使用了DOS命令行来解析,然后调用解析结果. 首先是执行DOS命令: procedure CheckResu

  • Delphi实现截屏存盘的方法

    本文实例讲述了Delphi实现截屏存盘的方法.分享给大家供大家参考.具体分析如下: 该实例可实现截取屏幕,并保存为JPEG文件格式的功能. procedure TForm1.ScreenCap(LeftPos,TopPos,RightPos,BottomPos:integer); var RectWidth,RectHeight:integer; SourceDC,DestDC,Bhandle:integer; Bitmap:TBitmap; MyJpeg: TJpegImage; Stream

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

    Delphi 中内存映射对于大文件的使用 平时很少使用大文件的内存映射,碰巧遇到了这样的要求,所以把过程记录下来,当给各位一个引子吧,因为应用不算复杂,可能有考虑不到的地方,欢迎交流. 对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如2G或者更多,文件流就不行了,所以要使用API的内存映射的相关方法,即使是内存映射,也不能一次映射全部文件的大小,所以必须采取分块映射,每次处理一小部分. 先来看几个函数 CreateFile :打开文件 GetFileSize : 获取文件尺

  • 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 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如下为一个对比实验. 复制代码 代码如下: package test; import java.io.BufferedInputStream;  import java.io.FileInputStream;  import java.io.FileNotFoundException;  import

  • Linux 中清空或删除大文件内容的五种方法

    在 Linux 终端下处理文件时,有时我们想直接清空文件的内容但又不必使用任何Linux命令行编辑器 去打开这些文件.那怎样才能达到这个目的呢?在这篇文章中,我们将介绍几种借助一些实用的命令来清空文件内容的方法. 注意: 由于再Linux中一切皆文件,你需要时刻注意,确保你将要清空的文件不是重要的用户文件或者系统文件.清空重要的系统文件或者配置文件可能会引发严重的应用失败或者系统错误. 提示:在下面的示例中,我们将使用名为 access.log 的文件来作为示例样本. 1. 通过重定向到 Nul

  • 在ASP.NET中支持断点续传下载大文件(ZT)源码

    IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务端都要添加这两个响应头,以保证客户端和服务端将此下载识别为可以断点续传的下载: Accept-Ranges:告知下载客户端这是一个可以恢复续传的下载,存放本次下载的开始字节位置.文件的字节大小: ETag:保存文件的唯一标识(我在用的文件名+文件最后修改时间,以便续传请求时对文件进行验证): Las

  • 解决IIS8.5中ASP上传大文件出现 Request 对象 错误 ASP 0104 : 80004005 错误的方法

    今天将一台服务器升级到了最新的IIS8.5,然后部署网站,上面有一个比较老的ASP站点发现了问题,上传稍大一点的文件就出错,传个几十K的文件到没有问题,我们都知道IIS默认上传文件大小是30M,而现在上传1M都有问题,下面给出解决方法. 上传不了的出错提示为:Request 对象 错误 'ASP 0104 : 80004005',原因是站点ASP的"限制属性"设置中"最大请求实体主体限制"默认只允许200K,将其改大一点就可以了,相关截图如下所示: 图中最大请求实体

  • 详解在Linux中清空或删除大文件内容的5种方法

    有时,在处理Linux终端中的文件时,您可能希望清除文件的内容,而无需使用任何Linux命令行编辑器打开它.怎么能实现这一目标?在本文中,我们将借助一些有用的命令,通过几种不同的方式清空文件内容. 警告:在我们继续查看各种方法之前,请注意,因为在Linux中一切都是文件,所以必须始终确保要清空的文件不是重要的用户或系统文件.清除关键系统或配置文件的内容可能会导致致命的应用程序/系统错误或故障. 就像刚刚说的,下面是从命令行清除文件内容的方法. 重要说明:出于本文的目的,我们access.log在

  • 解决PhpMyAdmin中导入2M以上大文件限制的方法分享

    要处理这个问题,经过一番研究发现,有2种方法: 方法一: 找到php.ini搜索这3个地方 upload_max_filesize , memory_limit 和 post_max_size将他们后面的值修改成大于你需要导入的数据库大小就好了.然后重启的PHP环境. 方法二:以phpMyAdmin-3.1.0-all-languages为例,我的安装目录E:\wwwroot\phpMyAdmin\ 1.在 phpmyadmin目录里新建一个目录 upload. 2.打开phpmyadmin,找

  • C#利用缓存分块读写大文件

    C#利用缓存分块读写大文件,供大家参考,具体内容如下 在日常生活中,可能会遇到大文件的读取,不论是什么格式,按照储存文件的格式读取大文件,就会在Buffer中看到相关的文件头合内容, 以一次.txt文件存取为例. using System.IO; 首先创建demo文件,此处文件大小没关系,只是演示 private void button2_Click(object sender, EventArgs e) { using (FileStream fsWrite = new FileStream(

  • Vue+Node实现大文件上传和断点续传

    目录 源代码 Blob.slice 切片 初始化文件内容 FormData.append() 大文件上传 断点续传 代码 创建切片 源码 worker 线程通讯的逻辑 断点续传 秒传 源代码 断点续传.分片上传.秒传.重试机制 文件上传是开发中的难点, 大文件上传及断点续传 难点中的细节及核心技术点. element-ui 框架的上传组件,是默认基于文件流的. 数据格式:form-data: 传递的数据: file 文件流信息:filename 文件名字 通过 fileRead.readAsDa

随机推荐