我们在使用kbmmw的REST 服务时,经常会下载和上传大文件。例如100M以上的。kbmmw的rest服务中
提供标准的文件下载,上传功能,基本上就是打开文件,发送,接收,没有做特殊处理。这些对于文件比较小的
时候,问题不大,但是如果文件比较大,就会占用大量的服务器内存,导致服务器出现问题或者不响应。
为了解决这个问题,我们需要对文件上传、下载做特殊处理。以便节省服务器端的内存。
由于下载大文件有其他的一些方法,例如可以单独建立一个iis,apache,nginx等,或者可以利用httpsys 的静态文件
下载功能(以后有机会的话,我来讲一下)。
今天来实现以下大文件上传的方法,(当然,你也可以参照这个做一个大文件下载的功能)。主要思路是按照kbmmw 本身的
文件服务原理,把大文件切成小块,然后再上传。
首先在服务器端做一个上传文件服务。
[kbmMW_Rest('method:post, path:uploadfile,anonymousResult:False,freeResult:true')]
[kbmMW_Method]
function uploadfile( [kbmMW_Rest('value: "$filepath", required: true')] const filepath:string; [kbmMW_Rest('value: "$token", required: true')] const token:string; [kbmMW_Rest('value: "$position", required: true')] const position:string; [kbmMW_Rest('value: "$size", required: true')] const size:string; [kbmMW_Rest('value: "$final", required: true')] const final:string):string;
function TkbmMWCustomHTTPSmartService1.uploadfile(const filepath, token,
position, size, final: string): string;
const
UPLOADPATH='d:\upload\';
MaxFileSize=**; //上传文件大小不能超过200M
errmsg='ERROR:';
var
fa:TkbmMWFileAccessPermissions;
h:THandle;
FileToken:integer;
path:string;
p:Pbyte;
// l:integer;
ref:TkbmMWFileReference;
fofs,sz,newofs,maxsize:int64;
bGC:boolean;
FFinal:boolean;
begin
if not Assigned(FilePool) then
begin
result:=errmsg+'No FilePool defined.';
exit;
end; if filepath='' then
begin
result:=errmsg+'No filepath.';
exit;
end;
if token='' then
begin
result:=errmsg+'No token.';
exit;
end; if position='' then
begin
result:=errmsg+'No position.';
exit;
end; fofs:=strtoint(position); if size='' then
begin
result:=errmsg+'No size.';
exit;
end; sz:=strtoint(size); if sz>** then
begin
result:=errmsg+'blocksize too big.';
exit;
end; if ((final<>'') and (final<>'')) then
begin
result:=errmsg+'final error.';
exit;
end; if final='' then
ffinal:=False
else
ffinal:=True; FileToken:=strtoint(token); path:= UPLOADPATH+filepath;
// Determine file mode.
if FileToken=- then
begin
// Check if file already exists and not overwrite permissions.
if FileExists(path) and (not (mwfapOverwrite in fa)) then
begin
result:=errmsg+'Permission denied';
exit;
end;
end; // Append block to file.
bGC:=false;
ref:=FilePool.Access(path,mwfamOpenWrite,FileToken,h);
try
ref.DuringUpdate:=true; maxsize:=MaxFileSize;
if FOfs>= then
begin
if (maxsize>) and (FOfs+sz>maxSize) then
begin
result:=errmsg+'File too big';
exit;
end; if FileSeek(h,FOfs,)< then
begin
result:=errmsg+'Cant position in file';
exit; end;
end
else
begin
newofs:=FileSeek(h,,);
if newofs< then
begin
result:=errmsg+'Cant append to file';
exit;
end; if (maxsize>) and (newofs+sz>maxSize) then
begin
result:=errmsg+'File too big';
exit;
end;
end; // Write requeststream to file.
p:=RequestStream.Memory;
if FileWrite(h,p^,sz)<>sz then
begin
ref.Invalidate; ref.DeleteOnGC:=true;
bGC:=true; result:=errmsg+'写文件失败.';
exit;
end;
finally
if FFinal then
ref.DuringUpdate:=false;
FilePool.ReleaseAccess(ref,h,FFinal);
if bGC then
FilePool.GarbageCollect;
end;
Result:=FileToken.ToString ; end;
编译运行即可。
客户端我们就直接增加一个上传过程。
procedure TForm1.Button3Click(Sender: TObject);
const
FBlockSize=**; baseurl='http://127.0.0.1/xalionrest';
basepath='d:\';
var
HttpClient:TNetHTTPClient;
requrl:string;
filetoken:string;
resp:IHTTPResponse;
final:string;
terminate:boolean;
Stream:TFileStream;
fm:integer;
position,sz:string;
pct:integer;
RequestStream:Tmemorystream;
LocalPath,filename:string;
n,bs,ofs:integer; begin filename:= 'delphi5.rar';
localpath:=basepath+filename; fm:=fmOpenRead+fmShareDenyWrite;
Stream:=TFileStream.Create(LocalPath,fm); HttpClient:= TNetHTTPClient.create(nil); RequestStream:= Tmemorystream.Create; filetoken:='-1';
final:='';
try
while true do
begin
if Stream.Size= then
pct:=
else
pct:=trunc((Stream.Position / Stream.Size) * ); position:=Stream.Position.ToString;
sz:=Stream.Size.ToString; RequestStream.Clear;
n:=FBlockSize;
ofs:=Stream.Position;
bs:=Stream.Size-ofs;
if bs<= then break;
if bs<=n then
begin
n:=bs;
final:='';
end;
RequestStream.CopyFrom(Stream,n); RequestStream.Position:=;
try requrl:= baseurl+'/uploadfile?'+'filepath='+filename+'&token='+filetoken+'&position='+position+'&size='+n.ToString+'&final='+final;
resp:=httpclient.Post(requrl,RequestStream);//
filetoken:=resp.ContentAsString(); if pos('ERROR:',filetoken)> then
begin
showmessage(filetoken);
exit; end; except
showmessage('上传失败!');
exit;
end; if n<FBlockSize then
break; end; showmessage('上传成功!'); finally Stream.Free; HttpClient.Free; RequestStream.free;
end; end;
运行起来。
我们看看服务器端的内存占用。
你可以看见服务的内存增长了,但是远远小于文件的大小。