使用内存映射文件读写大文件

使用内存映射文件读写大文件
  文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类。一般来说,这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。使用字符串变量的方法不仅会加重内存的负担,而且会Unicode和ASCII码的转换会把你弄得焦头烂额。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,比I/O读写要快20倍,所谓I/O操作不是对外围设备直接进行操作,而是对设备与cpu连接的接口电路的操作。而映射文件的方法就是对磁盘直接进行操作。
  内存映射文件无非就是那些文件中的数据被直接映射到进程地址空间中去的文件,与虚拟内存有类似的地方是,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。
  首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。
  CreateFileMapping()在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间,实际上相当于加载文件中指定的数据到内存中。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。
  实际上操作文件映射对象就相当于操作VC++文件读写方式下的文件内部指针。
  而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有2^32 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
  1)映射从文件开头的映像;
  2)对该映像进行访问;
  3)取消此映像;
  4)映射一个从文件中的一个更深的位移开始的新映像;
  5)重复步骤2,直到访问完全部的文件数据。
示例代码:
  在本例中,首先通过GetFileSize()得到被处理文件长度(64位)的高32位和低32位值。然后在映射过程中设定每次映射的块大小为1000倍的分配粒度(系统的数据分块大小),如果文件长度小于1000倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()、MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。

// 创建文件对象
HANDLE hFile = ::CreateFile(strFile, GENERIC_READ,FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE("创建文件对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 创建文件映射对象
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, , , NULL);
if (hFileMap == NULL)
{
TRACE("创建文件映射对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 得到系统分配粒度
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD dwGran = SysInfo.dwAllocationGranularity;
// 得到文件尺寸
DWORD dwFileSizeHigh;
__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
qwFileSize |= (((__int64)dwFileSizeHigh) << );///MSDN
// 偏移地址
__int64 qwFileOffset = ;
__int64 T_newmap = * dwGran;
// 块大小
DWORD dwBlockBytes = * dwGran;//文件数据分段大小
if (qwFileSize - qwFileOffset < dwBlockBytes)
dwBlockBytes = (DWORD)qwFileSize;
// 映射视图
char *lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(qwFileOffset >> ), (DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockBytes);
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失败,错误代码:%d ", GetLastError());
return;
}
// 关闭文件对象
CloseHandle(hFile);
///////////读文件数据
while(qwFileOffset < qwFileSize)
{
/******************** 读文件 ***************************/
//read_eh(&lpbMapAddress)读取已映射到内存的数据,并将文件指针作相应后移(lpbMapAddress++),返回指针偏移量
qwFileOffset = qwFileOffset + read_eh(&lpbMapAddress); //修改偏移量
if (qwFileOffset > T_newmap)
{//当数据读到90%时,为防数据溢出,需要映射在其后的数据 T_newmap
UnmapViewOfFile(lpbMapAddress);//释放当前映射
if ((DWORD)(qwFileSize - T_newmap) < dwBlockBytes)
dwBlockBytes = (DWORD)(qwFileSize - T_newmap);
lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(T_newmap >> ), (DWORD)(T_newmap & 0xFFFFFFFF),dwBlockBytes);
// 修正参数
lpbMapAddress = lpbMapAddress + qwFileOffset - T_newmap;
T_newmap =T_newmap + * dwGran;
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失败,错误代码:%d ", GetLastError());
return;
}
}
}
//释放最后数据块映射
UnmapViewOfFile(lpbMapAddress);
// 关闭文件映射对象句柄
CloseHandle(hFileMap);

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

http://blog.csdn.net/bdmh/article/details/6369250
平时很少使用大文件的内存映射,碰巧遇到了这样的要求,所以把过程记录下来,当给各位一个引子吧,因为应用不算复杂,可能有考虑不到的地方,欢迎交流。
对于一些小文件,用普通的文件流就可以很好的解决,可是对于超大文件,比如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 := ;
offset := ;
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,);
if FFile_Handle = INVALID_HANDLE_VALUE then Exit;
//获取文件尺寸
filesize := GetFileSize(FFile_Handle,nil);
//创建映射
FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,,,nil);
if FFile_Map = then Exit;
//此处我们已10倍blocksize为一个数据块来映射,如果文件尺寸小于10倍blocksize,则直接映射整个文件长度
if filesize div blocksize > then
readlen := *blocksize
else
readlen := filesize;
for i := to FInfoList.Count - do
begin
list.Delimiter := ':';
list.DelimitedText := FInfoList.Strings[i];
//取得长度,我这里做了解析,因为我存储的信息为 a:b:c 这种类型,所以以:号分隔
len := StrToInt(list.Strings[]);
interval := StrToInt(list.Strings[]);
if (i = ) or (totallen+len >=readlen) then
begin
//如果已读取的长度加上即将要读取的长度大于 10倍blocksize,那么我们要保留之前映射末尾的内容,以便和新映射的内容合并
if i > then
begin
offset := offset + readlen;
//写入临时流
tstream.Write(p^,readlen-totallen);
tstream.Position := ;
end;
//如果未读取的数据长度已经不够一个分配粒度,那么就直接映射剩下的长度
if filesize-offset < blocksize then
readlen := filesize-offset;
//映射,p是指向映射区域的指针
//注意这里第三个参数,一直设为0,这个值要根据实际情况设置
p := PChar(MapViewOfFile(FFile_Map,FILE_MAP_READ,,offset,readlen));
end;
//如果临时流中有数据,需要合并
if tstream.Size > 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 := ;
//将流保存成文件
stream.SaveToFile(IntToStr(i)+'.txt');
stream.Clear;
end;
finally
stream.Free;
tstream.Free;
CloseHandle(FFile_Handle);
CloseHandle(FFile_Map);
end;
end;

如何将一整个文件读入内存,文件大小有64M

function FastReadFile(FileName: string): Integer;
const
PAGE_SIZE = * ; //映射块大小不易过大,尽量以4k对齐
var
hFile: THandle;
szHigh,szLow: DWORD;
szFile,ps: Int64;
hMap: THandle;
hData: Pointer;
dwSize: Cardinal;
begin
Result := -;
hFile := ;
hMap := ;
hData := nil;
szHigh := ;
try
//打开已存在的文件,获得文件句柄
hFile := CreateFile(PChar(FileName),GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,
nil,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,);
if hFile = then
begin
Result := GetLastError;
Exit;
end;
//获取文件大小
hMap := ;
hData := nil;
szHigh := ;
szLow := GetFileSize(hFile,@szHigh);
szFile := szLow or (szHigh shl );
//创建映射句柄
hMap := CreateFileMapping(hFile, nil, PAGE_READWRITE, szHigh, szLow, nil);
if hMap = then
begin
Result := GetLastError;
Exit;
end;
ps := ;
//文件可能比较大,分块进行映射
while ps < szFile do
begin
//计算映射大小及位置
if szFile - ps > PAGE_SIZE then
dwSize := PAGE_SIZE
else
dwSize := szFile - ps;
szLow := ps and $FFFFFFFF;
szHigh := ps shr ;
//进行映射
hData := MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,szHigh,szLow,dwSize);
if hData = nil then
Break;
try
//此时文件偏移ps处的数据通过hData即可读取到,块大小为dwSize
//以下加上你读取的代码,可以做一个回调函数
//比如你要当前位置的数据(取文件数)拷到指定内存处 CopyMemory(目标地址指针,hData,dwSize);
//
finally
//移动文件偏移位置
ps := ps + dwSize;
//释放映射块
UnmapViewOfFile(hData);
hData := nil;
end;
end;
finally
//释放必要资源
if hData <> nil then
UnmapViewOfFile(hData);
if hMap <> then
CloseHandle(hMap);
if hFile <> then
CloseHandle(hFile);
end;
end;

 Delphi内存映射文件例子

unit  FileMap;  

interface  

uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,StdCtrls,Dialogs; type
TFileMap=class(TComponent)
private
FMapHandle:THandle; //内存映射文件句柄
FMutexHandle:THandle; //互斥句柄
FMapName:string; //内存映射对象
FSynchMessage:string; //同步消息
FMapStrings:TStringList; //存储映射文件信息
FSize:DWord; //映射文件大小
FMessageID:DWord; //注册的消息号
FMapPointer:PChar; //映射文件的数据区指针
FLocked:Boolean; //锁定
FIsMapOpen:Boolean; //文件是否打开
FExistsAlready:Boolean; //是否已经建立过映射文件
FReading:Boolean; //是否正在读取内存文件数据
FAutoSynch:Boolean; //是否同步
FOnChange:TNotifyEvent; //当内存数据区内容改变时
FFormHandle:Hwnd; //存储本窗口的窗口句柄
FPNewWndHandler:Pointer;
FPOldWndHandler:Pointer;
procedure SetMapName(Value:string);
procedure SetMapStrings(Value:TStringList);
procedure SetSize(Value:DWord);
procedure SetAutoSynch(Value:Boolean);
procedure EnterCriticalSection;
procedure LeaveCriticalSection;
procedure MapStringsChange(Sender:TObject);
procedure NewWndProc(var FMessage:TMessage);
public
constructor Create(AOwner:TComponent);override;
destructor Destroy;override;
procedure OpenMap;
procedure CloseMap;
procedure ReadMap;
procedure WriteMap;
property ExistsAlready:Boolean read FExistsAlready;
property IsMapOpen:Boolean read FIsMapOpen;
published
property MaxSize:DWord read FSize write SetSize;
property AutoSynchronize:Boolean read FAutoSynch write SetAutoSynch;
property MapName:string read FMapName write SetMapName;
property MapStrings:TStringList read FMapStrings write SetMapStrings;
property OnChange:TNotifyEvent read FOnChange write FOnChange;
end;
implementation
constructor TFileMap.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
FAutoSynch:=True;
FSize:=;
FReading:=False;
FMapStrings:=TStringList.Create;
FMapStrings.OnChange:=MapStringsChange;
FMapName:='Unique & Common name';
FSynchMessage:=FMapName+'Synch-Now';
if AOwner is TForm then
begin
FFormHandle:=(AOwner as TForm).Handle;
FPOldWndHandler:=Ptr(GetWindowLong(FFormHandle,GWL_wNDPROC));
FPNewWndHandler:=MakeObjectInstance(NewWndProc);
if FPNewWndHandler=nil then
raise Exception.Create('超出资源');
SetWindowLong(FFormHandle,GWL_WNDPROC,Longint(FPNewWndHandler));
end
else raise Exception.Create('组件的所有者应该是TForm');
end;
destructor TFileMap.Destroy;
begin
CloseMap;
SetWindowLong(FFormHandle,GWL_WNDPROC,Longint(FPOldWndHandler));
if FPNewWndHandler<>nil then
FreeObjectInstance(FPNewWndHandler);
FMapStrings.Free;
FMapStrings:=nil;
inherited destroy;
end;
procedure TFileMap.OpenMap;
var
TempMessage:array[..] of Char;
begin
if (FMapHandle=) and (FMapPointer=nil) then
begin
FExistsAlready:=False;
FMapHandle:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,,FSize,PChar(FMapName));
if (FMapHandle=INVALID_HANDLE_VALUE) or (FMapHandle=) then
raise Exception.Create('创建文件映射对象失败!')
else
begin
if (FMapHandle<>) and (GetLastError=ERROR_ALREADY_EXISTS) then
FExistsAlready:=True; //如果已经建立的话,就设它为TRUE;
FMapPointer:=MapViewOfFile(FMapHandle,FILE_MAP_ALL_ACCESS,,,);
if FMapPointer=nil then
raise Exception.Create('映射文件的视图到进程的地址空间失败')
else
begin
StrPCopy(TempMessage,FSynchMessage);
FMessageID:=RegisterWindowMessage(TempMessage);
if FMessageID= then
raise Exception.Create('注册消息失败')
end
end;
FMutexHandle:=Windows.CreateMutex(nil,False,PChar(FMapName+'.Mtx'));
if FMutexHandle= then
raise Exception.Create('创建互斥对象失败');
FIsMapOpen:=True;
if FExistsAlready then //判断内存文件映射是否已打开
ReadMap
else
WriteMap;
end;
end;
procedure TFileMap.CloseMap;
begin
if FIsMapOpen then
begin
if FMutexHandle<> then
begin
CloseHandle(FMutexHandle);
FMutexHandle:=;
end;
if FMapPointer<>nil then
begin
UnMapViewOfFile(FMapPointer);
FMapPointer:=nil;
end;
if FMapHandle<> then
begin
CloseHandle(FMapHandle);
FMapHandle:=;
end;
FIsMapOpen:=False;
end;
end;
procedure TFileMap.ReadMap;
begin
FReading:=True;
if(FMapPointer<>nil) then FMapStrings.SetText(FMapPointer);
end;
procedure TFileMap.WriteMap;
var
StringsPointer:PChar;
HandleCounter:integer;
SendToHandle:HWnd;
begin
if FMapPointer<>nil then
begin
StringsPointer:=FMapStrings.GetText;
EnterCriticalSection;
if StrLen(StringsPointer)+<=FSize
then System.Move(StringsPointer^,FMapPointer^,StrLen(StringsPointer)+)
else
raise Exception.Create('写字符串失败,字符串太大!');
LeaveCriticalSection;
SendMessage(HWND_BROADCAST,FMessageID,FFormHandle,);
StrDispose(StringsPointer);
end;
end;
procedure TFileMap.MapStringsChange(Sender:TObject);
begin
if FReading and Assigned(FOnChange) then
FOnChange(Self)
else if (not FReading) and FIsMapOpen and FAutoSynch then
WriteMap;
end;
procedure TFileMap.SetMapName(Value:string);
begin
if (FMapName<>Value) and (FMapHandle=) and (Length(Value)<) then
begin
FMapName:=Value;
FSynchMessage:=FMapName+'Synch-Now';
end;
end;
procedure TFileMap.SetMapStrings(Value:TStringList);
begin
if Value.Text<>FMapStrings.Text then
begin
if Length(Value.Text)<=FSize then
FMapStrings.Assign(Value)
else
raise Exception.Create('写入值太大');
end;
end;
procedure TFileMap.SetSize(Value:DWord);
var
StringsPointer:PChar;
begin
if (FSize<>Value) and (FMapHandle=) then
begin
StringsPointer:=FMapStrings.GetText;
if (Value<StrLen(StringsPointer)+) then
FSize:=StrLen(StringsPointer)+
else FSize:=Value;
if FSize< then FSize:=;
StrDispose(StringsPointer);
end;
end;
procedure TFileMap.SetAutoSynch(Value:Boolean);
begin
if FAutoSynch<>Value then
begin
FAutoSynch:=Value;
if FAutoSynch and FIsMapOpen then WriteMap;
end;
end;
procedure TFileMap.EnterCriticalSection;
begin
if (FMutexHandle<>) and not FLocked then
begin
FLocked:=(WaitForSingleObject(FMutexHandle,INFINITE)=WAIT_OBJECT_);
end;
end;
procedure TFileMap.LeaveCriticalSection;
begin
if (FMutexHandle<>) and FLocked then
begin
ReleaseMutex(FMutexHandle);
FLocked:=False;
end;
end;
//消息捕获过程
procedure TFileMap.NewWndProc(var FMessage:TMessage);
begin
with FMessage do
begin
if FIsMapOpen
if (Msg=FMessageID) and (WParam<>FFormHandle) then
ReadMap;
Result:=CallWindowProc(FPOldWndHandler,FFormHandle,Msg,wParam,lParam);
end;
end;end.

delphi 内存映射的更多相关文章

  1. delphi内存映射 与 映射数据获取

      一.原理     通过使用“内存映射文件”,实现内存共享 二.主要操作     共享内存结构: PShareMem = ^TShareMem; TShareMem = Record id:stri ...

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

    这篇文章主要介绍了Delphi 中内存映射对于大文件的使用的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下 Delphi 中内存映射对于大文件的使用 平时很少使用大文件的内存映射,碰巧遇到了 ...

  3. Delphi内存专题

    第一课: Windows 是多任务的操作系统, 一个任务就是一个应用(应用程序).一个应用占一个进程; 在一个进程里面, 又可以运行多个线程(所以就有了很多"多线程编程"的话题). ...

  4. 内存映射文件MemoryMappedFile使用

    参考资料: http://blog.csdn.net/bitfan/article/details/4438458 所谓内存映射文件,其实就是在内存中开辟出一块存放数据的专用区域,这区域往往与硬盘上特 ...

  5. JAVA NIO FileChannel 内存映射文件

      文件通道总是阻塞式的. 文件通道不能创建,只能通过(RandomAccessFile.FileInputStream.FileOutputStream)getChannel()获得,具有与File ...

  6. Python之mmap内存映射模块(大文本处理)说明

    背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力.关于sed的说明可以看了解sed的工作原理,本文将介绍通过 ...

  7. 使用ZwMapViewOfSection创建内存映射文件总结

    标 题: [原创]使用ZwMapViewOfSection创建内存映射文件总结 作 者: 小覃 时 间: 2012-06-15,02:28:36 链 接: http://bbs.pediy.com/s ...

  8. C#大文件读取和查询--内存映射

    笔者最近需要快速查询日志文件,文件大小在4G以上. 需求如下: 1.读取4G左右大小的文件中的指定行,程序运行占用内存不超过500M. 2.希望查询1G以内容,能控制在20s左右. 刚开始觉得这个应该 ...

  9. 用C#实现的内存映射

    当文件过大时,无法一次性载入内存时,就需要分次,分段的载入文件 主要是用了以下的WinAPI LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD ...

随机推荐

  1. 【Dart学习】--Dart之字符串(String)的相关方法总结

    字符串定义使用单引号或双引号 String a = "abcdefg"; String b = '; 创建多行字符串,保留内在格式使用三个单引号或三个双引号 创建多行字符串,保留内 ...

  2. uni-app获取元素宽高封装

    getElSize(id) { //得到元素的size return new Promise((res, rej) => { uni.createSelectorQuery().select(' ...

  3. 微信公众号ios10.1 版本白屏问题

    真机调试IOS 10.1.x的版本不支持fetch 所以一直loading显示白屏. 其他设备都没问题. 所以用主要属性window.fetch用来判断是否支持fetch 属性 import { ba ...

  4. android:layout_gravity和android:gravity的区别 (转)

    转:http://blog.csdn.net/shakespeare001/article/details/7843460 1.首先来看看android:layout_gravity和android: ...

  5. Linux命令 who

    who :显示当前登入系统的用户信息 显示的内容主要包括: 用户名,登录终端,上线时间,停留时间,动作,UID等 权限:所有使用者 语法: who  [option] ...[ file | arg1 ...

  6. 【lua学习笔记】——Notepad++ 设置运行 lua 和 python

    一.设置 run -> 设置 cmd /k lua "$(FULL_CURRENT_PATH)" & PAUSE & EXIT   二.原理:  cmd /k ...

  7. EOJ 1127. 多边形面积(计算几何)

    题目链接:1127. 多边形面积(计算几何) 题意 按逆时针顺序给出 \(n\) 个点的坐标,求这些点围成的多边形的面积. 思路 选择多边形上的一个点,然后每次枚举之后的两个点,计算叉积,注意要保留符 ...

  8. 数据库(一)—— MySQL介绍

    目录 MySQL介绍 一.MySQL版本 1.mysql主流版本 2.版本选择 二.MySQL连接与实例 1.MySQL的C/S结构 2.MySQL实例 三.mysql三层结构 1.连接层(连接上数据 ...

  9. Django框架(十九)—— drf:序列化组件(serializer)

    目录 序列化组件 一.利用for循环来实现序列化(繁琐) 二.利用Django提供的序列化组件(不可控需要的字段) 三.利用drf提供的序列化组件 1.基于Serializer类实现序列化--基本语法 ...

  10. Spring Boot控制上传文件大小

    spring: http: multipart: max-file-size: 5MB max-request-size: 20MB