Download file via REST service call?

Home Forums kbmMW Download file via REST service call?

Viewing 4 reply threads
  • Author
    Posts
    • #53430
      Decoder
      Participant

      I’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.

    • #53431
      Decoder
      Participant

      I 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.

       

      • #53434
        kimbomadsen
        Keymaster

        You can specify return file name with:

        SetResponseFileName(‘YourFileName’);

      • #53435
        kimbomadsen
        Keymaster

        HTTPResponseFromFile 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.

    • #53432
      Decoder
      Participant

      Ok, 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!

    • #53433
      kimbomadsen
      Keymaster

      Hi,

      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');
    • #53442
      Decoder
      Participant

      Thanks 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.

      • #53446
        kimbomadsen
        Keymaster

        Hi,

        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.

Viewing 4 reply threads
  • You must be logged in to reply to this topic.