Article
· Dec 5, 2023 3m read

Retrieving Base64 files from a POST call avoiding <MAXSTRING> errors

Welcome everybody!

In this short article I would like to present an example of use that surely many of you who work with IRIS as the backend for your web applications have faced on more than one occasion and it is how to send a file to your server from the frontend.

Generally the simplest way I have found to perform this task is to transform the file from the frontend to the Base64 format and make a POST call to our server attaching the Base64 obtained to a JSON message in which I indicate in a parameter the name of the file and in another the encoded data. Something similar to this:

{
    "fileData": "JVBERi0xLjQKJdPr6eEKMSAwIG...",
    "fileName": "example.pdf"
}

From my IRIS instance I have a configured web application to manage this POST calls from a class that extends %CSP.REST. This class has a class method to manage properly the POST. The code is something similar to this:

ClassMethod SaveFile() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the file data
        
        set dynamicBody = {}.%FromJSON(%request.Content)

        set dynamicStream = dynamicBody.%Get("fileData",,"stream<base64")
        
        set stream=##class(%Stream.FileBinary).%New()
        set sc=stream.LinkToFile("/shared/durable/"_dynamicBody.fileName)
        set sc=stream.CopyFromAndSave(dynamicStream)
       
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}

Let's explain in detail what this method is doing:

  1. We get the content sent in the body of the message and transform it to a %Library.DynamicAbstractObject that we can read as an object.
    set dynamicBody = {}.%FromJSON(%request.Content)
     
  2. This step is where the magic happens, we transform our Base64 into a %Stream type that we can obtain using the %Get method of the %Library.DynamicObject class.
  3. set dynamicStream = dynamicBody.%Get("fileData",,"stream<base64")
    As you can see, we are passing as an input parameter the type of transformation we want to perform, in our example from Base64 to Stream (stream<base64). What is the advantage of using this instruction? Well, we will not be limited by the MAXSTRING value, that is, if our Base64 file is very large we will never receive an exception because we have exceeded the maximum limit of the length allowed for a String.
  4. Finally, we create on our server the file that will contain the data sent in Base64 with the name defined in the call received and we write our object of type %Stream in it.
    set stream=##class(%Stream.FileBinary).%New()
    set sc=stream.LinkToFile("/shared/durable/"_dynamicBody.fileName)
    set sc=stream.CopyFromAndSave(dynamicStream)

As you can see, it is really easy to manage Base64 files from our server. If you have any questions, do not hesitate to write them in the comments section, I will be happy to answer them.

Discussion (2)2
Log in or sign up to continue

Hi Luis,

how can you possibly get a <MAXSTRING> error extracting a stream from a base64 encoded property that is imported from a JSON string that is limited itself by the MAXSTRING value?

In the line:

set bodyJson = %request.Content.Read()

You get the whole JSON into a string limited to string max size.

To avoid this, you can import directly the content stream into a dynamic object:

set dynamicBody = {}.%FromJSON(%request.Content)

Enrico