Tagged: Delphi kbmMw REST file download
- This topic has 7 replies, 2 voices, and was last updated 7 years ago by
kimbomadsen.
-
AuthorPosts
-
-
February 13, 2019 at 20:59 #53430
Decoder
ParticipantI’m trying to distribute an application file via our kbmMW REST service. Do you have any documentation on how this can be done? Do I need to create a separate File Service using the file pool components? Or is there a way to do it via HTTPS inside of a REST service?
Here’s an example of what I tried to do:
type
[kbmMW_Service(‘name:MyRESTService, flags:[listed]’)]
[kbmMW_Rest(‘path:/myrest’)]
TMyRESTFunctionsService = class(TkbmMWCustomHTTPSmartService)…
[kbmMW_Rest(‘method:get, path:MyApplicationFile, anonymousResult:true, responseMimeType:”application/octet-stream”, fileName:”MyAppFile.zip”‘)]
function MyApplicationFile: TStream;…
function TMyRESTFunctionsService.MyApplicationFile: TStream;
begin
// return .zip file
Result := TMemoryStream.Create;
TMemoryStream(Result).LoadFromFile(ExtractFileDir(Application.ExeName) + ‘\AppFileDir\MyAppFile.zip’);
end;Obviously the ‘fileName’ attribute on the service call won’t work, just pseudo code for how to identify the file name for the file download client. How can I do this?
Thanks.
-
February 13, 2019 at 22:59 #53431
Decoder
ParticipantI found this:
function HTTPResponseFromFile(const APath:string; const AAsFileName:string; const AMapPath:boolean = true):variant; overload;
…in the public interface of TkbmMWCustomHTTPService. I added a file pool to my smart service, creating it in the constructor with the service as the owner, and set the properties to match the properties in Demo\FileTransferStress\Server\Unit1.pas, and finally I assigned the file pool component to the smart service’s filepool property.
The test file store is a linux folder masked 777. When I call the service method, the response I get is:
“Cant access file:<file path as designated at runtime>”.
I checked the path, it’s totally valid. What have I missed?
Thanks.
-
February 14, 2019 at 03:38 #53434
kimbomadsen
KeymasterYou can specify return file name with:
…
SetResponseFileName(‘YourFileName’); -
February 14, 2019 at 03:43 #53435
kimbomadsen
KeymasterHTTPResponseFromFile will always default assume that the file must be placed in the path defined in the servicedefinition’s RootPath. The optional AMapPath argument is true in this case.
If you create a method that an enduser can call with a specific filename, and you have AMapPath set to false. Then it is a security hole, because a user can request any file on your server or a network drive accessible from the server.
Hence if you choose to use AMapPath:=false, you definitely should not allow a user to provide any filename/path for the request, or you should make your own bullet proof file path mapping.
-
-
February 14, 2019 at 02:04 #53432
Decoder
ParticipantOk, figured that out, it’s the AMapPath: Boolean = True param in HTTPResponseFromFile that was messing me up.
Now I get the file, but the client treats it as a web page, even though I’ve specified the content type as responseMimeType:”application/octet-stream”,
and displays the content of the zip file in the browser view.
So I guess I’m looking for the param, attribute, call, switch, component, property etc. that tells the request/response mechanism that this is a file, and specifies the file name.
Been browsing through your demos, but have not found anything that would demonstrate how to do this via a REST request.
Help!
-
February 14, 2019 at 03:31 #53433
kimbomadsen
KeymasterHi,
The reason is that HTTPResponseFromFile will attempt to detect the mime type itself, and override the setting defined in the attribute.
Unfortunately zip files are not well defined mimetype wise. Some claim they should be returned as application/zip, some as application/x-zip some as application/x-zip-compressed and so on. There are a myriad of variations, depending on use of browser and operating system version.
kbmMW default detects zip files as mimetype application/zip, which is the mimetype returned when using HTTPResponseFromFile.
There are various ways to change the mimetype (in no particular order):
1) Redefine the mimetype mapping in the OnSetupServiceDefinition event of the kbmMWServer instance:
procedure TForm1.serverServiceDefinition(Sender: TObject; ServiceDefinition: TkbmMWCustomServiceDefinition); begin // Example of how to remap a file extension mimetype for a specific service. if (ServiceDefinition is TkbmMWHTTPServiceDefinition) and (TkbmMWHTTPServiceDefinition(ServiceDefinition).Name='SMARTDEMO') then begin TkbmMWHTTPServiceDefinition(ServiceDefinition).MimeTypes.Values['ZIP']:='application/octect-stream'; end; end;2) Use a variant of HTTPResponseFromFile that do not override response mimetype:
var mimetype,charset:string; begin // Various ways to get a file. // 1. Get the file and return it as the mimetype and charset defined in the REST attribute. // The file path will extend from one of the RootPath matching the file type Result:=HTTPResponseFromFile('somefile.zip',mimetype,charset); // 2. Get the file and return it as the mimetype defined for ZIP files. // The file path will extend from one of the RootPath matching the file type // Result:=HTTPResponseFromFile('somefile.zip'); // 3. Get the file and return it as the mimetype defined for ZIP files. // The file path will be absolute // Result:=HTTPResponseFromFile('somefile.zip',false); // It is possible to map for example .zip file extension to a different mimetype // by updating the service definition MimeTypes list before use.3) Set mimetype after the fact:
Result:=HTTPResponseFromFile('somefile.zip'); SetResponseMimeType('application/octet-stream'); -
February 19, 2019 at 19:13 #53442
Decoder
ParticipantThanks Kim. By the time you posted this I had figured it out. Good example though.
In researching this I found the following mime type:
vnd.android.package-archive
The reason this is relevant is because my ‘zip’ file is an apk. Which is essentially a zip file, with a custom extension. I didn’t find that mime type in kbmMWCustomHTTPService.pas, procedure TkbmMWHTTPServiceDefinition.SetupStandardMimeTypes.
Is there a location where it would make sense to add this mime type? I did get it working with the following code:
Result := HTTPResponseFromFile(‘AppFileDir\AppFile.apk’,’AppFile.apk’);
SetResponseFileName(‘AppFile.apk’);
SetResponseMimeType(‘vnd.android.package-archive’);
SetResponseContentType(‘UTF-8’);It seems that Android likes that MimeType ‘more better’ than application/octet-stream.
Thanks again Kim.
-
February 20, 2019 at 10:06 #53446
kimbomadsen
KeymasterHi,
The place you do it there is fine!
If you want to add an entry to the services mimetype table you can also do that, using the event OnSetupServiceDefinition in the TkbmMWServer instance. The example I showed above can be used.
-
-
-
AuthorPosts
- You must be logged in to reply to this topic.
