熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

用Delphi 6開發ASP上傳組件詳解

2013-11-23 17:43:25  來源: Delphi編程 

  文件上傳是WEB開發中經常要用到的功能但ASP本身和內置的組件都不支持文件上傳功能網上流傳的一些第三方組件雖然能夠解決這個問題但大多是要收費的更別說Open Source了本文將詳細剖析WEB文件上傳的原理以及一步步指導讀者如何用Delphi開發一個ASP上傳組件

  源碼和demo我已經發布在個人主頁上http://wwwwushuangnet.


  Html文件分析

  首先我們來看一個html文件源碼文件名是testhtm功能是提供用戶上傳的界面

<html>
<body>
<center>
   <form name=mainForm enctype=multipart/formdata
action=testasp method=post>
    <input type=file name=mefile><br>
     <input type=hidden name=a value=fdsaf>
     <input type=hidden name=a value=fdsaf>
     <input type=hidden name=a value=fdsaf>
     <input type=hidden name=a value=fsdfsdsaf>
     <input type=hidden name=a value=這個是這個>
     <input type=text name=a value=fdsaf>
   <input type=submit name=ok value=OK>
   </form>
</center>
</body>
</html>

  這個文件裡包含了一個名為mainForm的form以及隨手寫的一些input域注意這個form和一般的form有兩個不同的地方一是它有一個type=file的域沒有value用浏覽器打開這個文件時這個域會表現為一個右側有浏覽字樣的文件輸入框用戶可以通過它來選擇本地硬盤上的文件二是form有一個特殊的屬性enctype=multipart/formdata這個屬性告訴浏覽器要上傳二進制文件並進行相應編碼

  這種編碼會產生什麼樣的表單信息呢?讓我們來看看testasp也就是接受表單的asp文件的源碼它非常簡單

<%
formsize=requesttotalbytes   獲得表單原始信息的長度
formdata=requestbinaryread(formsize)   讀取表單原始信息

responsebinarywrite formdata  返回表單原始信息
%>

  如讀者在注釋中了解的這段代碼的功能是將表單的原始信息返回讓我們來看看它的運行效果將這兩個文件置於web目錄下訪問testhtm在文件輸入框中選擇一個文件(我選了一個jpg圖片不過最大不要太大)提交然後可以看到這樣一堆亂七八糟的信息

de ContentDisposition: formdata;
name=mefile; filename=C:\Documents and Settings\aaa\My Documents\My
Pictures\zzjhjpg ContentType: image/pjpeg (作者注以下為亂碼)
de ContentDisposition: formdata;
name=a fdsaf de ContentDisposition:
formdata; name=a fdsaf de
ContentDisposition: formdata; name=a fdsaf
de ContentDisposition: formdata;
name=a fsdfsdsaf de
ContentDisposition: formdata; name=a 這個是這個
de ContentDisposition: formdata;
name=a fdsaf de ContentDisposition:
formdata; name=ok OK de

  這就是用multipart/formdata方式編碼的表單原始信息其中那一段看起來是亂碼的部分就是jpg圖片的編碼
  
  分析一下這段信息的格式  

de 這是各個域之間的分隔符
ContentDisposition: formdata; 說明這是表單中的域
name=mefile; 域的名稱
filename=C:\Documents and Settings\aaa\My Documents\My Pictures\zzjhjpg
傳文件在本地硬盤上的名稱
ContentType: image/pjpeg 文件類型
後面是文件本身的數據

  其它各個域的信息也可以以此類推

  眾所周知在ASP中使用request對象可以訪問用戶提交表單的各個域因為request對象會對原始的表單信息進行解析提取出表單中每個域的值但是request並不能解析這multipart/formdata格式的表單信息這就是ASP不能直接支持文件上傳的原因所在讀者可以試試在testasp中用request(mefile)這樣的格式是不能讀取到正確的信息的
  
  問題的症結已經找到解決的思路也很簡單用Delphi開發一個COM組件接受這種原始表單信息將各個域一一提取出來返回給asp文件也就是完成request對象沒有完成的功能

  二用Delphi開發組件

  Delphi對開發ASP組件提供了極好的支持大大簡化了我們的開發過程

  啟動Delphi 選擇FileNewOtherActiveXActiveX Library這樣就建立了一個ActiveX庫將此Library改名為myobj存盤選擇FileNewOtherActiveXActive Server Object在CoClassname中填入upfile確定這時會跳出一個標題為myobjtlb的對話框這是Delphi特有的以可視化方式編輯COM接口的功能用Delphi開發過COM的讀者應該比較熟悉

  在myobj下的名為Iupfile的Interface下添加個屬性和一個方法如果不懂得如何操作請參見Delphi參考書的相關部分按F可以看到生成的相應的myobj_tlbpas文件其中的Iupfile接口應該是這個樣子

  Iupfile = interface(IDispatch)
    [{CDEBAAEAEB}]
    procedure OnStartPage(const AScriptingContext: IUnknown); safecall;
    procedure OnEndPage; safecall;
    function  Get_Form(Formname: OleVariant): OleVariant; safecall;
    function  Get_FileName: OleVariant; safecall;
    function  Get_FileSize: Integer; safecall;
    procedure FileSaveAs(FileName: OleVariant); safecall;
    function  Get_FileData: OleVariant; safecall;
    function  Get_FileType: OleVariant; safecall;
    property Form[Formname: OleVariant]: OleVariant read Get_Form;
    property FileName: OleVariant read Get_FileName;
    property FileSize: Integer read Get_FileSize;
    property FileData: OleVariant read Get_FileData;
    property FileType: OleVariant read Get_FileType;
  end;

  其中的OnStartPage方法和OnEndPage方法是Delphi默認生成的其它的是手動加入的

  切換到unitpas(也是Delphi自動生成的)改名為upfilepas存盤可以看到存在一個Tupfile類的聲明它是繼承自TASPObject類和Iupfile接口的Delphi 已經自動生成了相應的代碼接下來的任務就是實現這個接口

  除了完成Iupfile接口中的屬性和方法之後還需要補充一些東西以便完成我們的任務最終的Tupfile類的聲明如下

  Tupfile = class(TASPObject Iupfile)
  public
  protected
    procedure OnEndPage; safecall;  //頁面開始
    procedure OnStartPage(const AScriptingContext: IUnknown); safecall;  //頁面
結束
    procedure FileSaveAs(Filename: OleVariant); safecall;  //保存文件
    function Get_Form(Formname: OleVariant): OleVariant; safecall;  //
    function Get_FileName: OleVariant; safecall; 
    function Get_FileSize: Integer; safecall;
    function Get_FileData: OleVariant; safecall;
    function Get_FileType: OleVariant; safecall;
  private
    FContentData:string;
    FFileDataFFileNameFFileType:string;
    FFormInfo:TStringList;
    function instr(strstr:string;startpos:integer):integer;
    procedure AnalyFormData(content:string);
  end;

  下面我們來一一分析這些成員的具體實現

procedure TupfileOnStartPage(const AScriptingContext: IUnknown);
var
  AOleVariant : OleVariant;
  tmpvar : OleVariant;
  contentlength : integer;
  iDeliCountposposlastpos : integer;
  FDelimeter : string;
begin
  inherited OnStartPage(AScriptingContext);
  FFormInfo := TStringListCreate;

  contentlength := RequestTotalBytes;
  AOleVariant := contentlength;
  tmpvar := RequestBinaryRead(AOleVariant);
  for i := to contentlength do
  begin
    FContentData := FContentData + chr(byte(tmpvar[i]));
  end;

  pos := pos(##FContentData);
  FDelimeter := copy(FContentDatapos+);
  DeliCount := length(FDelimeter);
  lastpos := ;

  pos:=;
  while pos>=pos do
  begin
    pos := instr(FDelimeterFContentDatalastpos);
    if pos = then Break;
    pos := pos + DeliCount;
    pos := instr(FDelimeterFContentDatapos);
    AnalyFormData(copy(FContentDatapospospos));
    lastpos := pos;
  end;
end;

  前面說過OnStartPage方法是Delphi自動生成的在裝載頁面時發生在這個方法中我們完成一些初始化的任務讀取表單的原始數據解析表單中的域並存入相應的屬性中以備調用

  由於Delphi已經對ASP中的對象進行了很好的封裝所以即使在Delphi環境下也可以方便地調用它們就象在ASP中一樣例如RequestTotalBytes首先將原始表單數據讀入到一個OleViarians類型的tmpvar中然後通過一個循環將它轉換為Delphi中的string格式並存放在FContentData中

  接下來通過查找換行符解析出分隔符的內容和長度然後在一個循環中用AnalyFormData成員函數一一解析出每個域初始化工作就這樣完成了

  再看AnalyFormData函數的實現

procedure TupfileAnalyFormData(content: string);
var
  pospos:integer;
  FormNameFormValue:string;
  isFile:boolean;
begin
  isFile := false;
  pos := instr(name=content)+;
  pos := instr(contentpos);
  FormName := copy(contentpospospos);

  //檢查是否文件
  pos := instr(filename=contentpos+);
  if pos <> then
  begin
    isFile := true;
    pos := pos + ;
    pos := instr(contentpos);
    FFilename := copy(contentpospospos);
  end;

  pos := instr(####contentpos+)+;
  FormValue := copy(contentposlength(content)pos);

  if isfile then
  begin
    FFileData := FormValue;
    //查找文件類型信息
    pos := instr(ContentType: contentpos+);
    if pos <> then
    begin
      pos := pos + ;
      FFileType := copy(contentpospospos);
    end;
  end
  else
  begin
  FFormInfoadd(FormName+=+FormValue);
  end;
end;

  如注釋中所表達的AnalyFormData提取原始數據中的域如果是域是文件類型則將文件類型和文件數據分別放入FFileType和FFileData中如果是其它類型則將名稱和值放入一個TStringlist類型的FFormInfo中FFormInfo中維護著除文件類型外的所有域的信息名稱=值的格式存放
  
function TupfileGet_Form(Formname: OleVariant): OleVariant;
begin
    Result := FFormInfoValues[Formname];
end;

  這個函數返回域的值只需要簡單地調用FFormInfo的values方法就可以得到相應的值這是在Tstringlist類內部實現的

function TupfileGet_FileName: OleVariant;
begin
  Result := ExtractFileName(FFileName);
end;


function TupfileGet_FileSize: Integer;
begin
  Result := length(FFileData);
end;

function TupfileGet_FileData: OleVariant;
var
  i:integer;
begin
  Result := VarArrayCreate( [length(FFileData)] varByte );
  for i := to length(FFileData) do
  begin
    Result[i] := Byte(FFileData[i+]);
  end;
end;

  這三個函數分別返回文件的名稱大小數據要注意的是在返回文件數據時必須進行相應的轉換將Delphi中的string類型轉換為OleVariant類型
  
procedure TupfileFileSaveAs(Filename: OleVariant);
var
  fsout:TFileStream;
  i:integer;
  afile:file of byte;
begin
  fsout := TFileStreamCreate(Filenamefmcreate);
  for i := to length(FFileData) do
  begin
    fsoutWrite(Byte(FFileData[i]))
  end;
  fsoutFree;
end;

  這個方法將文件保存到服務器上的磁盤

  編譯myobj這個project得到一個myobjdll文件開發工作就此完成

  使用ASP上傳組件
  
  在命令行下輸入regsvr myobjdll彈出一個對話框告訴你組件已經注冊如果找不到regsvrexe這個文件它在windows\system或winnt\system目錄下

  將本文開頭提到的testasp文件修改為如下內容

<%建立對象
Set upfile = ServerCreateObject(myobjupfile)

獲得表單對象
responsewrite upfileform(a)&<br>
responsewrite upfileform(a)&<br>
responsewrite upfileform(a)&<br>
responsewrite upfileform(a)&<br>
responsewrite upfileform(a)&<br>
responsewrite upfileform(a)&<br>

獲得文件大小
responsewrite 文件字節數&upfilefilesize&<br>
獲得文件類型
responsewrite 文件類型&upfilefiletype&<br>

獲得文件名保存文件
upfilefilesaveas(ServerMapPath()+upfilefilename)

set upfile = nothing
%>

  再次訪問testhtm提交表單現在你可以看到相關的返回信息並且在服務器上testasp所處的目錄下找到上傳的文件

  這個組件只能上傳單個文件但根據同樣的原理一次上傳多個文件的功能也是不難實現的有興趣的讀者可以自行嘗試


From:http://tw.wingwit.com/Article/program/Delphi/201311/24952.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.