Return an image from a Delphi REST server and show

2019-04-07 01:24发布

问题:

When you return an image using a file stream object in a Delphi rest server, it will not display in a browser. Here is an example method that returns an image:

function TServerClass.Image: TFileStream;
begin
  Result := TFileStream.Create('pathtofile\image.png', fmOpenRead or fmShareDenyNone);
end;

回答1:

The problem is that Delphi REST server always sets the content type to text/html. This confuses the browser when you send other types of content. It is a bug, since most responses are json, which means the most sensible default content type should be application/json.

Fortunately there is a way to override the content type from within the server method.

You need to add Data.DBXPlatform to the uses list of your implementation.

This unit contains the function GetInvocationMetadata, which gives access to the response that is being build. It returns a TDSInvocationMetadata object which among varius other useful properties has the ResponseContentType property.

Setting this property overrides the Content-Type header that the method returns in the http response.

The given example becomes:

function TServerClass.Image: TFileStream;
begin
  Result := TFileStream.Create('pathtofile\image.png', fmOpenRead or fmShareDenyNone);
  GetInvocationMetadata.ResponseContentType := 'image/png';
end;

Now the result image will be displayed properly in the browser.



回答2:

I've found this problem too trying to download different file types (png, pdf, xlsx, docx, etc...) from DataSnap REST server (Delphi XE3) to a JavaScript web client. Some browsers (es.: FireFox) will take correct action anyway, but not all. Internet Explorer doesn't recognize the proper action for the downloaded file without correct content-type. The @Anders solution initially seems to work for me because I was working with PDF and Firefox. But when I've tested on IE (and others) and with different extensions, the files where not recognised. Using FireBug I've seen that Content-Type was always "text/html" and not the assigned one using

GetInvocationMetadata.ResponseContentType := '...my assigned content type ...';

The workaround found working for me is:

In ServerMethodsUnit

var
   ContentTypeHeaderToUse: string;  // Global variable

TServerMethods1.GetFile(params: JSON):TStream;
begin
   .... processing ....
   ContentTypeHeaderToUse := '...' (assign correct content type).
end;

In WebModuleUnit

procedure TWebModule1.WebModuleAfterDispatch(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  if ContentTypeHeaderToUse<>'' then begin
    Response.ContentType := ContentTypeHeaderToUse;
    ContentTypeHeaderToUse := ''; // Reset global variable
  end;
end;

I used a similar solution for assigning Content-Disposition too. This is a useful header key in order to set file name to download and attachment/inline mode. With this one the code is:

procedure TWebModule1.WebModuleAfterDispatch(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  if ContentDispositionHeaderToUse<>'' then begin
    Response.SetCustomHeader('content-disposition',ContentDispositionHeaderToUse);
    ContentDispositionHeaderToUse := '';
  end;
  if ContentTypeHeaderToUse<>'' then begin
    Response.ContentType := ContentTypeHeaderToUse;
    ContentTypeHeaderToUse := '';
  end;
end;

Assign ContentDispositionHeaderToUse into the server methods implementation.

EDIT

This workaround doesn't work in ISAPI DLL on IIS with data compression enabled! With no data compressione (local debuggin IIS) the response header is:

Connection  close
Content-Disposition inline; filename="Privacy-0.rtf.pdf"
Content-Length  150205
Content-Type    application/pdf; charset=ISO-8859-1
Pragma  dssession=28177.371935.39223,dssessionexpires=1200000

but with production enabled IIS the response comes with:

Content-Encoding    gzip
Content-Length  11663
Content-Type    text/html
Date    Thu, 11 Sep 2014 21:56:43 GMT
Pragma  dssession=682384.52215.879906,dssessionexpires=1200000
Server  Microsoft-IIS/7.5
Vary    Accept-Encoding
X-Powered-By    ASP.NET

Content-disposition and content-type assigned in DataSnap code are not surfaced.