mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-16 08:15:53 +01:00
278 lines
7.9 KiB
ObjectPascal
278 lines
7.9 KiB
ObjectPascal
unit Net.CrossHttpRouterDirUtils;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Classes,
|
|
System.Generics.Collections,
|
|
System.Generics.Defaults,
|
|
System.NetEncoding,
|
|
System.IOUtils,
|
|
Utils.Utils;
|
|
|
|
function BuildDirList(const ARealPath, ARequestPath, AHome: string): string;
|
|
|
|
implementation
|
|
|
|
{$region 'Dir辅助代码'}
|
|
type
|
|
THttpFileEntry = record
|
|
Name: string;
|
|
Size: Int64;
|
|
Time: TDateTime;
|
|
Directory: Boolean;
|
|
ReadOnly: Boolean;
|
|
SysFile: Boolean;
|
|
Hidden: Boolean;
|
|
end;
|
|
|
|
function BuildDirList(const ARealPath, ARequestPath, AHome: string): string;
|
|
function SmartSizeToStr(const ABytes: Int64): string;
|
|
const
|
|
KBYTES = Int64(1024);
|
|
MBYTES = KBYTES * 1024;
|
|
GBYTES = MBYTES * 1024;
|
|
TBYTES = GBYTES * 1024;
|
|
PBYTES = TBYTES * 1024;
|
|
begin
|
|
if (ABytes < KBYTES) then
|
|
Result := Format('%dB', [ABytes])
|
|
else if (ABytes < MBYTES) then
|
|
Result := Format('%.2fK ', [ABytes / KBYTES])
|
|
else if (ABytes < GBYTES) then
|
|
Result := Format('%.2fM ', [ABytes / MBYTES])
|
|
else if (ABytes < TBYTES) then
|
|
Result := Format('%.2fG ', [ABytes / GBYTES])
|
|
else if (ABytes < PBYTES) then
|
|
Result := Format('%.2fT ', [ABytes / TBYTES])
|
|
else
|
|
Result := Format('%.2fP ', [ABytes / PBYTES]);
|
|
end;
|
|
|
|
function FormatDirEntry(const APath: string; const F: THttpFileEntry): string;
|
|
var
|
|
Attr, Link, NameString, SizeString: string;
|
|
begin
|
|
if (F.Name = '.') or (F.Name = '..') then
|
|
begin
|
|
Result := '';
|
|
Exit;
|
|
end;
|
|
|
|
// drwsh
|
|
Attr := '-rw--';
|
|
if F.Directory then
|
|
begin
|
|
Attr[1] := 'd';
|
|
SizeString := '';
|
|
NameString := '<font color="#0074d9">' + F.Name + '</font>';
|
|
end
|
|
else
|
|
begin
|
|
SizeString := SmartSizeToStr(F.Size);
|
|
NameString := F.Name;
|
|
end;
|
|
|
|
if F.ReadOnly then
|
|
Attr[3] := '-';
|
|
|
|
if F.SysFile then
|
|
Attr[4] := 's';
|
|
|
|
if F.Hidden then
|
|
Attr[5] := 'h';
|
|
|
|
if (APath[Length(APath)] = '/') then
|
|
Link := TNetEncoding.URL.Encode(F.Name)
|
|
else
|
|
Link := APath + '/' + TNetEncoding.URL.Encode(F.Name);
|
|
|
|
Result :=
|
|
'<TD WIDTH="55%" NOWRAP><A HREF="' + Link + '">' + NameString + '</A></TD>' +
|
|
'<TD WIDTH="5%" ALIGN="LEFT" NOWRAP>' + Attr + '</TD>' +
|
|
'<TD WIDTH="%15" ALIGN="right" NOWRAP>' + SizeString + '</TD>' +
|
|
'<TD WIDTH="5%" NOWRAP></TD>' +
|
|
'<TD WIDTH="20%" NOWRAP>' + FormatDateTime('YYYY-MM-DD HH:NN:SS', F.Time) + '</TD>';
|
|
end;
|
|
|
|
function PathToURL(const APath, AHome: string): string;
|
|
function _NormalizePath(const APathStr: string): string;
|
|
begin
|
|
Result := APathStr.Replace('\/', '/').Replace('\\', '/').Replace('//', '/');
|
|
if (Result = '') then
|
|
Result := '/'
|
|
else
|
|
begin
|
|
if (Result.Chars[0] <> '/') then
|
|
Result := '/' + Result;
|
|
if (Result.Chars[Result.Length - 1] <> '/') then
|
|
Result := Result + '/';
|
|
end;
|
|
end;
|
|
var
|
|
LPath, LHome, LSubPath: string;
|
|
LPathArr, LHomeArr: TArray<string>;
|
|
I: Integer;
|
|
begin
|
|
LPath := _NormalizePath(APath);
|
|
LHome := _NormalizePath(AHome);
|
|
|
|
LPathArr := LPath.Split(['/', '\'], TStringSplitOptions.ExcludeEmpty);
|
|
LHomeArr := LHome.Split(['/', '\'], TStringSplitOptions.ExcludeEmpty);
|
|
if Length(LHomeArr) > Length(LPathArr) then Exit('');
|
|
|
|
I := 0;
|
|
while True do
|
|
begin
|
|
if (I >= Length(LPathArr)) or (I >= Length(LHomeArr))
|
|
or not SameText(LPathArr[I], LHomeArr[I]) then Break;
|
|
Inc(I);
|
|
end;
|
|
|
|
Result := Format('<A HREF="%s"><b><font color="#ff4136">Home</font></b></A> / ',
|
|
[LHome]);
|
|
LSubPath := LHome;
|
|
|
|
while True do
|
|
begin
|
|
if (I >= Length(LPathArr)) then Break;
|
|
|
|
LSubPath := LSubPath + LPathArr[I] + '/';
|
|
Result := Result + Format('<A HREF="%s"><b><font color="#0074d9">%s</font></b></A> / ',
|
|
[LSubPath, LPathArr[I]]);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
var
|
|
Status: Integer;
|
|
F: TSearchRec;
|
|
DirList, FileList: TList<THttpFileEntry>;
|
|
Data: THttpFileEntry;
|
|
LComparer: IComparer<THttpFileEntry>;
|
|
i: Integer;
|
|
Total: Cardinal;
|
|
TotalBytes: Int64;
|
|
HTML: string;
|
|
begin
|
|
LComparer := TDelegatedComparer<THttpFileEntry>.Create(
|
|
function(const Left, Right: THttpFileEntry): Integer
|
|
begin
|
|
Result := TUtils.CompareStringIncludeNumber(
|
|
Left.Name, Right.Name);
|
|
end);
|
|
|
|
DirList := TList<THttpFileEntry>.Create;
|
|
FileList := TList<THttpFileEntry>.Create;
|
|
Status := FindFirst(TPath.Combine(ARealPath, '*.*'), faAnyFile, F);
|
|
while Status = 0 do
|
|
begin
|
|
if (F.Name <> '.') and (F.Name <> '..') then
|
|
begin
|
|
Data.Name := F.Name;
|
|
Data.Size := F.Size;
|
|
Data.Time := F.TimeStamp;
|
|
Data.Directory := ((F.Attr and faDirectory) <> 0);
|
|
Data.ReadOnly := ((F.Attr and faReadOnly) <> 0);
|
|
Data.SysFile := ((F.Attr and faSysFile) <> 0);
|
|
Data.Hidden := ((F.Attr and faHidden) <> 0);
|
|
|
|
if ((F.Attr and faDirectory) <> 0) then
|
|
DirList.Add(Data)
|
|
else
|
|
FileList.Add(Data);
|
|
end;
|
|
|
|
Status := FindNext(F);
|
|
end;
|
|
FindClose(F);
|
|
DirList.Sort(LComparer);
|
|
FileList.Sort(LComparer);
|
|
|
|
HTML :=
|
|
'<HTML>' +
|
|
'<HEAD>' +
|
|
'' +
|
|
'<STYLE TYPE="text/css">' +
|
|
'.dirline {font-family: "Microsoft Yahei",simsun,arial; color: #111111; font-style: normal;}' +
|
|
'.hline {height:0;overflow:hiddne;border-top:1px solid #C3C3C3}' +
|
|
'.vline {width:0;overflow:hiddne;border-left:1px solid #C3C3C3}' +
|
|
'a:link {text-decoration: none; color: #111111;}' +
|
|
'a:visited {text-decoration: none; color: #111111;} ' +
|
|
'a:hover {text-decoration: underline; color: #0000FF;}' +
|
|
'a:active {text-decoration: none; color: #111111;}' +
|
|
'</STYLE>' +
|
|
'<TITLE>文件列表</TITLE>' +
|
|
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' +
|
|
'</HEAD>' +
|
|
'<BODY>' +
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">' +
|
|
'<TR><TD>' + PathToURL(ARequestPath, AHome) + '<BR><BR></TD></TR></TABLE>';
|
|
|
|
TotalBytes := 0;
|
|
Total := DirList.Count + FileList.Count;
|
|
if Total <= 0 then
|
|
HTML := HTML + '<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER"><TR><TD><BR>空目录</TD></TR></TABLE>'
|
|
else
|
|
begin
|
|
HTML := HTML +
|
|
// 标题
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">' +
|
|
'<TR>' +
|
|
'<TD WIDTH="55%" NOWRAP>文件名</TD>' +
|
|
'<TD WIDTH="5%" ALIGN="LEFT" NOWRAP>属性</TD>' +
|
|
'<TD WIDTH="%15" ALIGN="right" NOWRAP>大小</TD>' +
|
|
'<TD WIDTH="5%" NOWRAP></TD>' +
|
|
'<TD WIDTH="20%" NOWRAP>修改时间</TD>' +
|
|
'</TR>' +
|
|
'</TABLE>' +
|
|
|
|
// 一条灰色横线
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">' +
|
|
'<TR><TD HEIGHT="3"><div class="hline"></div></TD></TR>' +
|
|
'</TABLE>' +
|
|
|
|
// 文件列表表格
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">';
|
|
|
|
for i := 0 to DirList.Count - 1 do
|
|
begin
|
|
Data := DirList[i];
|
|
HTML := HTML + '<TR>' + FormatDirEntry(ARequestPath, Data) + '</TR>';
|
|
end;
|
|
|
|
for i := 0 to FileList.Count - 1 do
|
|
begin
|
|
Data := FileList[i];
|
|
HTML := HTML + '<TR>' + FormatDirEntry(ARequestPath, Data) + '</TR>';
|
|
TotalBytes := TotalBytes + Data.Size;
|
|
end;
|
|
|
|
HTML := HTML + '</TABLE>' +
|
|
// 一条灰色横线
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">' +
|
|
'<TR><TD HEIGHT="3"><div class="hline"></div></TD></TR>' +
|
|
'</TABLE>' +
|
|
|
|
// 页脚统计信息
|
|
'<TABLE CLASS="dirline" WIDTH="90%" ALIGN="CENTER">' +
|
|
'<TR>' +
|
|
'<TD WIDTH="55%" NOWRAP>' + Format('目录: %d, 文件: %d', [DirList.Count, FileList.Count]) + '</TD>' +
|
|
'<TD WIDTH="5%" NOWRAP></TD>' +
|
|
'<TD WIDTH="%15" ALIGN="right" NOWRAP>' + SmartSizeToStr(TotalBytes) + '</TD>' +
|
|
'<TD WIDTH="5%" NOWRAP></TD>' +
|
|
'<TD WIDTH="20%" NOWRAP></TD>' +
|
|
'</TR>' +
|
|
'</TABLE>';
|
|
end;
|
|
|
|
FreeAndNil(DirList);
|
|
FreeAndNil(FileList);
|
|
|
|
HTML := HTML + '</BODY></HTML>';
|
|
Result := HTML;
|
|
end;
|
|
{$endregion}
|
|
|
|
end.
|