PASOE/REST/DOH - File Download

Posted by jts-law on 20-Feb-2018 10:29

Has anybody successfully configured a file download via PASOE REST using a Data Object Handler service?

In my interface I have the content type set to "multipart/form-data", and the output body is ABL type "CLASS OpenEdge.Net.MultipartEntity".  My current code is:

         COPY-LOB FROM FILE cTmpXlsx TO memptrTmp.
         /* Load file/memptr into a Memptr object (for MessagePart) */
         oMemptr = NEW Memptr(GET-SIZE(memptrTmp)).
         oMemptr:PutBytes(memptrTmp).

         poEntity = NEW MultipartEntity().
         poEntity:Boundary = GUID.
         
         oPart = NEW MessagePart('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':u, oMemptr).
         oHeader = HttpHeaderBuilder:Build('Content-Disposition':u)
                                    :Value(SUBSTITUTE('form-data; name="fileContents"; filename=&1':u, QUOTER("Test.xlsx")))
                                    :Header.
         poEntity:AddPart(oPart).
         oPart:Headers:Put(oHeader).
         
         RETURN 200.

I have a couple problems.  The first is that the file that is downloaded contains all of the part header information.  The second is that the downloaded filename is my URI/operation name and not the filename set in the Content-Disposition header.

Downloaded file contents:

--d2abaee5-58fa-13b3-3714-453b65266f8b 
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 
Content-Disposition: form-data; name="fileContents"; filename="Profit.xlsx"
[File contents]
--d2abaee5-58fa-13b3-3714-453b65266f8b--

Thoughts?

TIA

Louis Winter

Posted by Peter Judge on 07-Mar-2018 14:54

Hi Louis,
 
I’ve added an example at github.com/.../doh_named_file .  There are three examples in the code of how you can return the header and data. One returns a FileInputStream object, which I’d suggest since it has both the file contents and its name (which the Memptr does not).
 
I’m thinking about options for how to make it possible for you not to have to return the header manually.  So you’d just return the image object from you ABL code.
 
If you care to read more about the Content-Disposition header, per developer.mozilla.org/.../Content-Disposition, there are basically 2 parameters you can pass: inline and attachment. The latter is the ‘download’ option.
 
 
 

All Replies

Posted by Irfan on 20-Feb-2018 10:59

Hi Louis,

Here is an example I wrote to upload multipart data. In this example I am not uploading to database, but I am able to extract the payload correctly.

method override protected integer HandlePost(INPUT poRequest AS IWebRequest):
        define variable oResp as IHttpResponse no-undo.
        define variable oEntity as MultipartEntity no-undo.
        define variable oPart as MessagePart no-undo.
        define variable oEntityWriter as MessageWriter no-undo.
        define variable oHeader as HttpHeader no-undo.
        define variable response as WebResponse       no-undo.
        define variable writer   as WebResponseWriter no-undo.
        define variable cImageFileName as character no-undo.
        def var i as integer.
        def var memptr1 as class memptr.

        response = new WebResponse().
        response:StatusCode = 200.
        response:StatusReason = "OK".

         writer = new WebResponseWriter( response ).

       oEntityWriter = EntityWriterBuilder:Build(poRequest)
                                :Writer.

        oEntityWriter:Open().
        oEntityWriter:Write(poRequest:Entity).
        oEntityWriter:Close().


       message " My ContentType is "   poRequest:GetHeader("Content-Type"):Value.


       assign oEntity = cast(oEntityWriter:Entity, MultipartEntity).

        writer:write( "<HTML>" ).
        writer:write( "<HEAD>" ).
        writer:write( "<TITLE>Round Trip</TITLE>" ).
        writer:write( "</HEAD>" ).
        writer:write( "<BODY BGCOLOR=~"FFFFFF~">" ).
        writer:write( "<H2>Multipart Request Payload</H2>" ).


      DO i = 1 TO oEntity:Size:
            oPart   = oEntity:GetPart(Integer(i)).
            oHeader = oPart:Headers:Get('Content-Disposition':u).
            cImageFileName = oHeader:GetParameterValue('filename':u).

            if type-of(oPart:Body,ByteBucket)
                then
            do:
                memptr1 = cast(oPart:Body, ByteBucket):GetBytes().
                writer:Write("<BR>element position is " + String(i)).
                writer:Write("<BR>Image name: " + cImageFileName).
                writer:Write("<BR>Image size: " + String(GET-SIZE(memptr1:value))).
                writer:write("<BR>Content-Type: " + oPart:ContentType).
                writer:write("<BR>").


               end.
               else do:

               writer:Write("<BR>element position is " + String(i)).
               writer:Write("<BR>Text Name: " + String(oPart:Body)).
               writer:write("<BR>Content-Type: " + oPart:ContentType).
               writer:write("<BR>").


            end.


     END.

        writer:write( "</BODY>" ).
        writer:write( "</HTML>" ).

        writer:close().
        writer:flush().

             return integer(StatusCodeEnum:OK).

    END METHOD.

Posted by jts-law on 20-Feb-2018 11:14

Thanks Irfan.  I have the upload working already, my issue now is downloading a file.  I've found download code (github.com/.../ImageWebHandler.cls) similar to yours that I've been trying to use, but it's not quite working.

I'm using a Data Object WebSpeed service which uses annotations to define the URIs.  I'm also using a custom .map file to define both my upload and download URIs.

All of the examples I've seen use a WebResponse and WebResponseWriter.  I'm not sure how these fit in with the PASOE REST service and how I would need to define my output argument.

Louis

Posted by Peter Judge on 21-Feb-2018 06:54

Louis,
 
I’d try a couple of things
- change the  response’s Content-Type to multipart/mixed
- change the Content-Disposition to “attachment; filename=”Test.xslx”)
- RETURN 0 from the method.

Posted by jts-law on 21-Feb-2018 16:05

Peter,

Sorry for the delay.  I tried the changes.  Returning 0 causes it to not download anything.  Changing this back to 200 results in the file to download.  The downloaded file is still the same, it contains the header information.  I captured this with fiddler and the following is what is shown for the raw response:

HTTP/1.1 200
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: multipart/mixed
Transfer-Encoding: chunked
Date: Wed, 21 Feb 2018 21:59:07 GMT

2000
--d58c7aac-9a72-66be-3714-e14ea1e9b748
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="Test.xlsx"

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
1259
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
--d58c7aac-9a72-66be-3714-e14ea1e9b748--


0

Any other ideas?

Louis

Posted by jts-law on 28-Feb-2018 15:10

Does anybody have any thoughts/ideas on how to get a file to download successfully from a Data Object Handler REST interface without using a WebResponse and WebResponseWriter?

Louis

Posted by Peter Judge on 01-Mar-2018 14:06

Louis,

I created an example of this at github.com/.../doh_multipart

LMK if that's what you're after (or not).

Posted by jts-law on 07-Mar-2018 08:33

Peter,

I changed my map to match your contentType and all arguments for my download function (DownloadAcctCat), commented out my method and added your ReadMultipart (renamed to DownloadAcctCat), and changed image file name to "progress-oe.png".  The test program returns 4 parts.  If I use the download URL in a browser I still get a file with the boundary and other information.

localhost:8810/.../DownloadProfitAcctCat

Returns a file named "DownloadProfitAcctCat" with the following content:

--my-part-bound

Content-Type: application/json

{"part-01":1}

--my-part-bound

Content-Type: image/png

‰PNG

[file contents...]

--my-part-bound

Content-Type: text/plain

this is part number 3

--my-part-bound

Content-Type: application/json

{"part-04":4}

--my-part-bound--

I also tried commenting out the entity:AddPart() calls for cnt = 0 and 1 (leaving just the actual file) and the download file is still named "DownloadProfitAcctCat" and it has the boundary and Content-Type lines.

With this example, I want to be able to call "localhost:8810/.../DownloadProfitAcctCat" from the browser and have it download a file named "progress-oe.png" that is an actual image file.

Louis

Posted by Peter Judge on 07-Mar-2018 11:40

Louis,

Do you care about multipart messages, or do you just want a file?

In both cases, you need a Content-Disposition  header that tells the client the name of the file. See github.com/.../ImageWebHandler.cls for an example.

Note that for a single message body, the Content-Disposition is "attachment" and for the multipart response, it's "form-data".

HTH

Posted by jts-law on 07-Mar-2018 13:29

Peter,

No, I don't care about multipart messages, I just want to download a single file.  I started with multipart simply because that's what I was working with for the upload and didn't know exactly what all to change in the map and signature for anything else.

In my original attempt I had the Content-Disposition and it was still downloading a file with the name of the service (DownloadProfitAcctCat).  I also had the output of the method as a MultipartEntity, which doesn't seem to work.

The following is how I was trying to set the header:

        oHeader = HttpHeaderBuilder:Build('Content-Disposition':u)
                                   :Value(SUBSTITUTE('attachment; filename=&1':u, QUOTER("Test.xlsx"))):Header.

In order to download the file directly without the multipart message, what should the signature of the method be?

Louis

Posted by jts-law on 07-Mar-2018 14:37

Peter,

Just wanted to let you know quick that I think I'm close.  I changed my map file to:

        "/DownloadProfitAcctCat": {
          "GET": {
            "contentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "entity": {
              "name": "UploadProfitAcctCat",
              "function": "DownloadAcctCat",
              "arg":
                [
                  {
                    "ablName": "",
                    "ablType": "INTEGER",
                    "ioMode": "RETURN",
                    "msgElem": {"type": "StatusCode", "name": null}
                  },
                  {
                    "ablName": "poMemptr",
                    "ablType": "CLASS OpenEdge.Core.Memptr",
                    "ioMode": "OUTPUT",
                    "msgElem": {"type": "BODY", "name": null}
                  }
                ]
            }
          }
        }

The method is now:

   METHOD PUBLIC INT DownloadAcctCat(OUTPUT poMemptr AS OpenEdge.Core.Memptr):
      DEF VAR cTmpXlsx        AS CHAR                                    NO-UNDO.
      DEF VAR memptrTmp       AS MEMPTR                                  NO-UNDO.

      cTmpXlsx = "Test.xlsx".
      COPY-LOB FROM FILE cTmpXlsx TO memptrTmp.
      poMemptr = NEW Memptr(GET-SIZE(memptrTmp)).
      poMemptr:PutBytes(memptrTmp).
      SET-SIZE(memptrTmp) = 0.

      RETURN 200.
   END METHOD.

Now, my contents of the download file are correct but the file name is "DownloadProfitAcctCat.xlsx".  Since I'm now returning a Memptr, how do I set the Content-Disposition?

Louis

Posted by Peter Judge on 07-Mar-2018 14:54

Hi Louis,
 
I’ve added an example at github.com/.../doh_named_file .  There are three examples in the code of how you can return the header and data. One returns a FileInputStream object, which I’d suggest since it has both the file contents and its name (which the Memptr does not).
 
I’m thinking about options for how to make it possible for you not to have to return the header manually.  So you’d just return the image object from you ABL code.
 
If you care to read more about the Content-Disposition header, per developer.mozilla.org/.../Content-Disposition, there are basically 2 parameters you can pass: inline and attachment. The latter is the ‘download’ option.
 
 

Posted by jts-law on 08-Mar-2018 08:13

Peter,

Thanks for all your help, my download is now working great!

I ended up using a combination of your last examples.  Since I am actually generating the file to be downloaded, I still wanted to be able to return the HTTP status so I have my return value as the StatusCode, and now have 2 output (HttpHeader and Memptr).  I tried using the FileInputStream first which works, but my generated temp file doesn't get removed, I'm sure that was because the DOH had the file open for the response.  The Memptr works better for my situation.

One last question, are the valid "type" and "name" values for the msgElem in the map file documented anywhere?

Louis

Posted by Peter Judge on 08-Mar-2018 08:36

Hey Louis,
 
I’m glad you’ve got it working. If you’re generating the file on the fly, or reading from a db, then the Memptr approach is probably the better one to use.
The list of valid values for the msgElem type is in the ElementTypeEnum ( shipped in $DLC/src/netlib/OpenEdge.net.pl or more easily read at https://github.com/consultingwerk/ADE-Sourcecode/blob/master/src/netlib/OpenEdge/Web/DataObject/ElementTypeEnum.cls ).  The value of the “name” property varies depending on the type.
 
For the others, the “value” depends on the type.
- body: we ignore it so null is used by convention
- headers: the name of an HTTP header
- cookies: the name of a cookie
- field: the name of a field. In JSON this would be a property name. In the future, this might be Content-ID of a multipart message part.
- query: the name of a query parameter string
- status code: a status code :)
- constant: a constant value to pass into the ABL code
 
Note that some are input only, some are output only and some are used for input and outpuit.
 
In general terms we have the documentation of the DOH’s mapping on our backlog.
 

Posted by jts-law on 08-Mar-2018 08:43

Peter,

Thanks for the info.  I'm not planning on changing my code now that it's working, it's just nice to know what our options are.

Louis

This thread is closed