Jump to content

Serving up a file from a non UniGUI server path (proxy-like)


wprins

Recommended Posts

Hi,

 

I want to serve up files from non UniGUI server based locations (or maybe even dynamic generation), kind of web-service style.  

 

The background is that we need the UniGUI client's browser to directly download or access files and data from locations NOT directly under where the server is running.  These files need to be made available in the short term via the UniGUI app I'm working on, initially via direct "download".  For example, PDF documents (or excel, or word, or whatever) that should be served to the client browser, as simple downloads. 

 

(I'm aware of the Gnostice StarDocs option and will be looking into this for a more seamless "in application" viewing approach, but in the meantime though it ought to be possible to just "directly" serve up files to the client/browser, where the document/file is not a location "under" the UniGUI server, or as I've said, may potentially not exist as a file at all but might be generated on the fly.)

 

I've looked at the relevant demo's and there's obviously the PDF viewer demo, which however relies on the correspondence between "UniServerModule.FilesFolderPath" and "UniServerModule.FilesFolderURL) to effectively directly serve up PDF's hosted in a "known" location to the TUniURLFrame.  Again, this works because you can construct a URL that corresponds to a location accessible underneath the UniGUI server and pass this to the TUniURLFrame.  

 

But what if this is not the case? That is, what would be the best UniGUI way to in effect "proxy" the file access request and return (in effect), say,  a filestream from a location or source determined/managed by the UniGUI service?

 

For example under Web Broker, one could generate what appears to be a file response on the fly, by handling the Response yourself in the relevant "OnAction" handler in the WebModule (kind of the equivalent of the UniGUI MainModule), doing e.g. something like the following in the relevant Action' OnAction() handler:

//Web Broker style of dynamic generation of data that looks like file download to browser
Response.CustomHeaders.Values['Content-Disposition'] := 'attachment; filename='+FormatDateTime('yyyy-mm-dd', SelectedDate)+'.csv';
Response.ContentStream := TStringStream.Create;
{dump some stuff into TStringStream}
Response.SendResponse;
Handled := True;

Similarly if this was a Datasnap server, one could presumably (not tested) do something similar using a TDSHTTPServiceFileDispatcher component and overriding the handling in BeforeDispatch(), where you are also given the Response object and have the opportunity to set Handled to True.

 

Is there a component or a way to allow similarly overriding request processing from withing a user's session (e.g. not from the server's perspective -- it has OnHTTPCommand and OnHTTPDocument but these are not session/user specific so doesn't help here)?  

 

In an ideal world I'd have access to the Request and Response objects (like in DataSnap, WebBroker or Indy) from the context of the MainModule "OnHandleRequest" event.   As it stands, unless I've missed something, in the MainModule's OnHandleRequest() event, while you are handed a reference to the relevant Session object (as a TObject, presumably castable TUniGUISession), it however doesn't seem to expose a way of directly handling/overriding/modifying the underlying HTTP Response object as outlined above  (though there is a "Handled" var parameter...)?  

 

Thanks!

Link to comment
Share on other sites

As a possible alternative workaround, what is the most effective way to copy a file into the UniGUI cache and obtain the relevant URL to hand to a TUniGUIURLFrame?  (Though copying every file into the UniGUI cache would not be my preferred solution due to the duplication and overheads of having possibly multiple copies of files lying around...) 

Link to comment
Share on other sites

I've come up with a somewhat less than ideal solution but in case anyone's interested:

 

In part of the application where the file to download is identified (a click on a TUniTreeview node), I generate a GUID.

 

Against the GUID I register the local (network) file path for the file to be served in a dictionary held on the server module.

 

Then I setup the URL on the TUniURLFrame (or TUniLabel with the .TextConversion property set as txtHTML) as something like URL := '/guid?g='+GUIDToString(FileGUID);

 

When the UniURLFrame is shown, or the user clicks (say) the HTML link in the label representing the file, the browser then fetches from the server with the above manufactured GUID based URL.  

 

Inside the ServerModule.HTTPCommand event handler I in turn check for document requests starting with '/guid'.  If found the request is manually handled, something like this:

 

  if ARequestInfo.Document.StartsWith('/guid') then
  begin
    LGUIDStr := ARequestInfo.Params.Values['g'];
    if FGUIDCache.ContainsKey(LGUIDStr) then
      LFilename := FGUIDCache[LGUIDStr]
    else
      raise EProgrammerNotFound.Create('Failed to find '+LGUIDStr);
    if FileExists(LFileName) then
      LFileStream := TFileStream.Create(LFileName, fmOpenRead OR fmShareDenyNone)
    else
      raise EProgrammerNotFound.Create('Failed to find '+LFileName);
    
    AResponseInfo.CustomHeaders.Values['Content-Disposition'] := 'attachment; filename='+ExtractFileName(LFileName);
    AResponseInfo.ContentStream := LFileStream; //Freed by Indy?
    Handled := True;
    //LFileStream.Free;  //Freed by Indy, I think.
  end;

This has the result of prodding the browser into downloading the file.  

 

One concern I have with this is that it basically operates outside the user's session, so one needs to do something about authentication/authorisation since in principle anyone that can access the service can request the file without any blocks whatsoever, as it stands.

 

This is why I was really wanting to do this from the user session {mainmodule} as then you know that the request is legit.  It is of course unlikely that someone will guess the GUID, but still.  The request ought to be rejected outright if not originating from the app itself.  If UniGUI had support to identify requests originating from its own URLFrame components (via say, AccessHeader token) and/or to feed through those requests so that they could be processed/handled in the MainModule OnHandleRequest() that would be rather better than being forced to handle them indirectly in the main server thread/module...  (If this is in fact possible and I've mised it please forgive my ignorance and point me in the right direction!)

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...