Pele
Pele

Reputation: 71

Delphi indy send file to client and run

I'm making a small server type application.

In a web browser, I want the user to enter the server address and login, and then my server has to return a path to a file that exists on the server. The file has to be run on the user's local computer (these are small Excel files, so it will probably work quickly).

Is it possible to do? Do I have to download the file first and then run it?

The file should run automatically after login, so my server must send the file to the client machine and run it on the client machine.

Can you show me a small example?

P.S. I use Indy components, but if someone has a better idea, I'm open to suggestions.

Upvotes: 1

Views: 1668

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598364

What you are asking for is technically doable in HTTP, as the response to any HTTP request can be the actual Excel file. For example:

Using HTTP authentication:

procedure TMyForm.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Document = '/myfile.xlsx' then
  begin
    if not ARequestInfo.AuthExists then
    begin
      AResponseInfo.AuthRealm := 'myserver';
      Exit;
    end;
    if not UserIsAuthenticated(ARequestInfo.AuthUsername, ARequestInfo.AuthPassword) then
    begin
      AResponseInfo.ResponseNo := 403;
      Exit;
    end;
    case ARequestInfo.CommandType of
      hcGET:
      begin
        AResponseInfo.SmartServeFile(AContext, ARequestInfo, '<path>\myfile.xlsx');
      end;
      hcHEAD:
      begin
        AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType('myfile.xlsx');
        AResponseInfo.ContentLength := FileSizeByName('<path>\myfile.xlsx');
        AResponseInfo.ContentDisposition := 'attachment; filename="myfile.xlsx";';
      end;
    else
      AResponseInfo.ResponseNo := 405;
    end;
  end else
  begin
    AResponseInfo.ResponseNo := 404;
  end;
end;

Using HTML webform authentication:

index.html

<html>
<head>
<title>login</title>
</head>
<body>
<form action="/login" method="POST">
Username: <input type="text" name="user"><br>
Password: <input type="password" name="pswd"><br>
<input type="submit" value="Submit"> <input type="reset" value="Clear">
</form>
</body>
</html>
procedure TMyForm.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Document = '/' then
  begin
    case ARequestInfo.CommandType of
      hcGET, hcHEAD:
      begin
        AResponseInfo.ContentType := 'text/html';
        if ARequestInfo.CommandType = hcGET then
          AResponseInfo.ContentStream := TIdReadFileExclusiveStream.Create('<path>\index.html')
        else
          AResponseInfo.ContentLength := FileSizeByName('<path>\index.html');
      end;
    else
      AResponseInfo.ResponseNo := 405;
    end;
  end
  else if ARequestInfo.Document = '/login' then
  begin
    if ARequestInfo.CommandType <> hcPOST then
    begin
      AResponseInfo.ResponseNo := 405;
      Exit;
    end;
    if not UserIsAuthenticated(ARequestInfo.Params.Values['user'], ARequestInfo.Params.Values['pswd']) then
    begin
      AResponseInfo.ResponseNo := 403;
      Exit;
    end;
    AResponseInfo.ServeFile(AContext, '<path>\myfile.xlsx');
  end else
  begin
    AResponseInfo.ResponseNo := 404;
  end;
end;

Alternatively:

// make sure to set TIdHTTPServer.SessionState=True...

procedure TMyForm.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  if ARequestInfo.Document = '/' then
  begin
    case ARequestInfo.CommandType of
      hcGET, hcHEAD:
      begin
        AResponseInfo.ContentType := 'text/html';
        if ARequestInfo.CommandType = hcGET then
          AResponseInfo.ContentStream := TIdReadFileExclusiveStream.Create('<path>\index.html')
        else
          AResponseInfo.ContentLength := FileSizeByName('<path>\index.html');
      end;
    else
      AResponseInfo.ResponseNo := 405;
    end;
  end
  else if ARequestInfo.Document = '/login' then
  begin
    if ARequestInfo.CommandType <> hcPOST then
    begin
      AResponseInfo.ResponseNo := 405;
      Exit;
    end;
    if ARequestInfo.Session = nil then
    begin
      IdHTTPServer1.CreateSession(AContext, AResponseInfo, ARequestInfo);
    end;
    if not UserIsAuthenticated(ARequestInfo.Params.Values['user'], ARequestInfo.Params.Values['pswd']) then
    begin
      AResponseInfo.Session.Content.Values['AuthOK'] := 'no';
      AResponseInfo.ResponseNo := 403;
      Exit;
    end;
    AResponseInfo.Session.Content.Values['AuthOK'] := 'yes';
    //AResponseInfo.Redirect('/myfile.xlsx');
    AResponseInfo.ResponseNo := 303;
    AResponseInfo.Location := '/myfile.xlsx';
  end
  else if ARequestInfo.Document = '/myfile.xlsx' then
  begin
    if ARequestInfo.AuthExists then
    begin
      if ARequestInfo.Session = nil then
      begin
        IdHTTPServer1.CreateSession(AContext, AResponseInfo, ARequestInfo);
      end;
      ARequestInfo.Session.Content.Values['AuthOK'] := iif(UserIsAuthenticated(ARequestInfo.AuthUsername, ARequestInfo.AuthPassword), 'yes', 'no');
    end;
    if (ARequestInfo.Session = nil) or (ARequestInfo.Session.Content.IndexOf('AuthOK') = -1) then
    begin
      //AResponseInfo.Redirect('/');
      AResponseInfo.ResponseNo := 303;
      AResponseInfo.Location := '/';
      Exit;
    end;
    if ARequestInfo.Session.Content.Values['AuthOK'] <> 'yes' then
    begin
      AResponseInfo.ResponseNo := 403;
      Exit;
    end;
    case ARequestInfo.CommandType of
      hcGET:
      begin
        AResponseInfo.SmartServeFile(AContext, ARequestInfo, '<path>\myfile.xlsx');
      end;
      hcHEAD:
      begin
        AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType('myfile.xlsx');
        AResponseInfo.ContentLength := FileSizeByName('<path>\myfile.xlsx');
        AResponseInfo.ContentDisposition := 'attachment; filename="myfile.xlsx";';
      end;
    else
      AResponseInfo.ResponseNo := 405;
    end;
  end else
  begin
    AResponseInfo.ResponseNo := 404;
  end;
end;

However, either way, the user's web browser would have to be configured beforehand to automatically open .xlsx files in Excel (or whatever viewer/editor they want), or at least to prompt the user whether to open the files. The server can't force the files to be opened automatically, that would be a breach of the user's security.

Upvotes: 3

Related Questions