2017-05-03 1 views
1

J'ai créé une application VCL et je dois créer un serveur HTTP qui fonctionne dans mon réseau. J'ai créé le code que vous pouvez voir ci-dessous:serveur Delphi IdHTTP charge html

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; 
    ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
var 
    a: TStringList; 
    count, logN: integer; 
begin 

if ARequestInfo.Document = '/' then 
    begin 

    AResponseInfo.ResponseNo := 200; 
    AResponseInfo.ContentText := IndexMemo.Lines.Text; 
    Memo1.Lines.Add(' Client: ' + ARequestInfo.RemoteIP); 

    end 
else 
    begin 

    AResponseInfo.ResponseNo := 200; 
    AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>'; 

    end; 

end; 

Maintenant, je ne dispose que d'un cas de test if ARequestInfo.Document = '/' then mais plus tard, je aurai besoin beaucoup d'entre eux. J'ai trouvé cette solution:

  1. Laisser un mémo sous la forme
  2. Ajouter le code HTML dans le mémo
  3. Chargez le texte de la note de service dans le ContextText

Je ne pense pas que c'est très efficace parce que je devrais laisser tomber 20 TMemo dans ma forme et le HTML sera difficile à maintenir. J'ai pensé que je pourrais charger les pages html avec le Deployment manager.

enter image description here

Dans le même dossier du projet Delphi J'ai créé un dossier appelé pages et il contiendra les fichiers html. Je ne sais pas comment charger les pages html avec un serveur HTTP indy, donc mes questions sont les suivantes:

  1. Dois-je stocker les pages html quelque part dans un dossier, puis les charger en utilisant indy?
  2. Puis-je charger des pages html avec indy qui sont inclus dans la page de déploiement?

Remarque: Je voudrais avoir un seul exe (ce qui est le serveur http) et non un dossier avec les fichiers exe + html. La solution que j'ai trouvée fonctionne plutôt bien car j'utilise beaucoup de TMemo pour stocker le code, mais ce n'est pas facile à maintenir.

Répondre

5

D'abord, le code que vous avez montré n'est pas adapté aux threads. TIdHTTPServer est un composant multithread, les événements OnCommand... sont déclenchés dans le contexte des threads de travail. Vous devez synchroniser avec le thread principal de l'interface utilisateur afin d'accéder aux commandes de l'interface utilisateur en toute sécurité, par exemple:

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; 
    ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
var 
    s: string; 
begin 
    if ARequestInfo.Document = '/' then 
    begin 
    TThread.Synchronize(nil, 
     procedure 
     begin 
     s := IndexMemo.Lines.Text; 
     Memo1.Lines.Add(' Client: ' + ARequestInfo.RemoteIP); 
     end 
    ); 

    AResponseInfo.ResponseNo := 200; 
    AResponseInfo.ContentText := s; 
    AResponseInfo.ContentType := 'text/plain'; 
    end 
    else 
    begin  
    AResponseInfo.ResponseNo := 404; 
    AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>'; 
    AResponseInfo.ContentType := 'text/html'; 
    end; 
end; 
  1. Dois-je stocker les pages html quelque part dans un dossier, puis les charger en utilisant indy?

  2. Puis-je charger des pages html avec indy qui sont inclus dans la page de déploiement?

Si vous voulez servir les fichiers du système de fichiers local, vous devez traduire la valeur de la propriété ARequestInfo.Document à un chemin de fichier local, puis vous pouvez:

  1. charge la demande déposer dans un TFileStream et l'affecter à la propriété AResponseInfo.ContentStream:

    procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; 
        ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
    var 
        str, filename: string; 
    begin 
        str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document; 
        TThread.Queue(nil, 
        procedure 
        begin 
         Memo1.Lines.Add(str); 
        end 
    ); 
    
        if TextStartsWith(ARequestInfo.Document, '/') then 
        begin 
        filename := Copy(ARequestInfo.Document, 2, MaxInt); 
        if filename = '' then 
         filename := 'index.txt'; 
    
        // determine local path to requested file 
        // (ProcessPath() is declared in the IdGlobalProtocols unit)... 
        filename := ProcessPath(YourDeploymentFolder, filename); 
    
        if FileExists(filename) then 
        begin 
         AResponseInfo.ResponseNo := 200; 
         AResponseInfo.ContentStream := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite); 
         AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType(filename); 
         Exit; 
        end; 
        end; 
    
        AResponseInfo.ResponseNo := 404; 
        AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>'; 
        AResponseInfo.ContentType := 'text/html'; 
    end; 
    
  2. passer le chemin du fichier à la méthode TIdHTTPResponseInfo.(Smart)ServeFile() et laissez-le gérer le fichier pour vous:

    procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; 
        ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
    var 
        str, filename: string; 
    begin 
        str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document; 
        TThread.Queue(nil, 
        procedure 
        begin 
         Memo1.Lines.Add(str); 
        end 
    ); 
    
        if TextStartsWith(ARequestInfo.Document, '/') then 
        begin 
        filename := Copy(ARequestInfo.Document, 2, MaxInt); 
        if filename = '' then 
         filename := 'index.txt'; 
    
        // determine local path to requested file... 
        filename := ProcessPath(YourDeploymentFolder, filename); 
    
        AResponseInfo.SmartServeFile(AContext, ARequestInfo, filename); 
        Exit; 
        end; 
    
        AResponseInfo.ResponseNo := 404; 
        AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>'; 
        AResponseInfo.ContentType := 'text/html'; 
    end; 
    

je voudrais avoir un seul exe (ce qui est le serveur http) et non un dossier avec fichiers exe + html.

Dans ce cas, enregistrez les fichiers HTML dans les ressources EXE à la compilation (en utilisant un fichier .rc, ou dialogue Resources and Images de l'EDI. Voir Resource Files Support pour plus de détails), puis traduire le ARequestInfo.Document dans un ID de ressource/nom que vous pouvez charger avec TResourceStream pour être utilisé comme l'objet AResponseInfo.ContentStream:

procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; 
    ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
var 
    str, resID: string; 
    strm: TResourceStream; 
begin 
    str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document; 
    TThread.Queue(nil, 
    procedure 
    begin 
     Memo1.Lines.Add(str); 
    end 
); 

    if TextStartsWith(ARequestInfo.Document, '/') then 
    begin 
    // determine resource ID for requested file 
    // (you have to write this yourself)... 
    resID := TranslateIntoResourceID(Copy(ARequestInfo.Document, 2, MaxInt)); 
    try 
     strm := TResourceStream.Create(HInstance, resID, RT_RCDATA); 
    except 
     on E: EResNotFound do 
     strm := nil; 
    end; 

    if strm <> nil then 
    begin 
     AResponseInfo.ResponseNo := 200; 
     AResponseInfo.ContentStream := strm; 
     AResponseInfo.ContentType := 'text/html'; 
     Exit; 
    end; 
    end; 

    AResponseInfo.ResponseNo := 404; 
    AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>'; 
    AResponseInfo.ContentType := 'text/html'; 
end; 
+0

Toujours le meilleur, vous avez résolu un gros problème ici. Je n'ai pas pensé au fait qu'il fonctionne sur un fil séparé. Merci beaucoup –

+0

d'où vient le TranslateIntoResourceID? Même chose avec TextStartsWith mais j'ai résolu rapidement en utilisant si ARequestInfo.Document.Chars [0] = '/' puis –

+0

"* d'où vient le TranslateIntoResourceID? *" - vous devez l'écrire vous-même. "* Même chose avec TextStartsWith *" - dans l'unité 'IdGlobal' d'Indy. –

4

Vous pouvez lire le contenu d'un fichier

procedure TForm2.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
    var Page : TStringStream; 
begin 
    Page := TStringStream.Create; 
    Page.LoadFromFile('put the file path here'); 
    AResponseInfo.ResponseNo := 200; 
    AResponseInfo.ContentStream := page; 
end; 

Vous pouvez lire le contenu d'une ressource, allez au menu du projet, des ressources et Imagens, ajoutez les ressources dont vous avez besoin.

procedure TForm2.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); 
     var page : TResourceStream; 
    begin 
     //home is the resource name 
     page := TResourceStream.Create(HInstance, 'home', RT_RCDATA); 
     AResponseInfo.ResponseNo := 200; 
     AResponseInfo.ContentStream := page; 
    end; 
+1

'' a TIdHTTPResponseInfo' (Smart) 'méthodes ServeFile() disponibles, vous n'avez pas besoin d'ouvrir et de lire les fichiers vous-même. –

+1

Vous devriez utiliser 'TFileStream' (ou' TIdReadFileExclusiveStream' d'Indy ou 'TIdReadFileNonExclusiveStream') au lieu de' TStringStream'. Il n'est pas nécessaire de charger le fichier en mémoire (et certainement pas en tant que ** chaîne **). Dans les deux exemples, n'oubliez pas de définir 'AResponseInfo.ContentType'. –