简体   繁体   中英

DataSnap REST JavaScript client-side – How to show pdf file data returned as TStream from a TServerMethods method

I've a DataSnap server method

function TServerMethods.GetFile(filename): TStream

returning a file. In my test case the file is a simple .PDF.

I'm sure this function works fine, as I'm able to open files on ObjectiveC client side app's where I've used my own http call to the DataSnap method (no Delphi proxy). The stream is read from ASIHttpRequest object and saved as local file, then loaded and regulary shown in standard pdf reader. I do not kown how exactly ASIHttpRequest manages the returned data.

But on JavaScript client side where I use standard

stream = ServerMethods().GetFile('test.pdf')

JavaScript function, as provided from DataSnap proxy itself, I do not figure out how to show the .pdf data to the user.

Using

window.open().document.write(stream);

a new browser window opens with textual raw data ( %PDF-1.5 %âãÏÓ 1 0 obj << /Type /Catalog /Pages 2 0 R …..)

With

window.open("data:application/pdf;base64," +stream);

I get an empty new browser page.

With

window.open("data:application/pdf," +stream);

or

document.location = 'data:application/pdf,'+encodeURIComponent(serverMethods().GetFile('test'));

I get an new browser page with pdf empry reader and alert “This PDF document could not be displayed correctly”

Nothing changes adding:

GetInvocationMetadata().ResponseContentType := 'application/pdf';

into the DataSnap function.

I've no other ideas...

EDIT

The task is for a general file download, not only PDF. PDF is a test only. GetFile have to manage .pdf, .xlsx, .docx, .png, .eml, etc...

Your server side code works as expected once you set the ResponseContentType. You can test this by calling the method directly from a browser. Change the class name to match the one you're using:

http://localhost:8080/datasnap/rest/TServerMethods1/GetFile/test.pdf

I'm sure there's a way to display the stream properly on the browser side, but I'm not sure what that is. Unless you're doing something special with the stream, I'd recommend getting the document directly or using a web action and getting out of the browser's way. Basically what mjn suggested.

I can think of a couple of solutions.

1) A quick way would be to allow access to the documents directly.

In the WebFileDispatcher, add a WebFileExtension. Select .pdf and it will fill in the mime type for you. Assuming your pdf documents are in the "docs" folder, the url might look like this:

http://localhost:8080/docs/test.pdf

2) I would probably add an action on the web module. It's a little more involved, but it also gives me more control.

Using this url:

http://localhost:8080/getfile?filename=test.pdf

And code like this in the web action handler (no checking or error handling). The Content-Disposition header suggests a file name for the downloaded file:

procedure TWebModule1.WebModule1GetFileActionAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  lStream: TMemoryStream;
  lFilename: string;
begin
  lFilename := Request.QueryFields.Values['filename'];
  lStream := TMemoryStream.Create;
  lStream.LoadFromFile('.\Docs\' + lFilename);
  lStream.Position := 0;
  Response.ContentStream := lStream;
  Response.ContentType := 'application/pdf';
  Response.SetCustomHeader('Content-Disposition',
    Format('attachment; filename="%s"', [lFilename]));
end;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM