Recent posts:
Recent replies:

Ok I got your method working now also. I can see the benefit of having the shell logs captured. It's great to have two options! Thanks.

So it turns out my original code and your suggested version worked after all, just needed a server reboot for the PATH to take affect! I just need to sort out user permissions around the account that can use $ZF(-100) and I think I'm all good!

Anyway, I thought i'd just write up my a way of doing this in case it's helpful to anyone.

Below is a REST class that takes a list of files in array and it will convert them to Basic64 using a temp path. Example payload in the code, there's few parameters you can mess around with.

In my actual code I get paths from a SOAP interfaces and need to convert the files back to website. Therefore I am attempting to do this at the last point so it doesn't produce audits/messages. I also don't want exceptions to ever go back to the client so I will always force 1x1 image to rendered but I've added the errors back into this example.

Include %occInclude /// <PRE style="font-size:0.8em;">  
/// ************************************************************************************************************************************
/// Class Name  : Demo.REST.Server
/// Description : Demo REST Service   
/// ************************************************************************************************************************************
/// </PRE>
/// 
/// Please note there are some limitations as string max is 3,641,144 characters. This is addressed in newer IRIS versions, see links below:
/// https://community.intersystems.com/post/size-limitation-tojson-large-stream
/// https://community.intersystems.com/post/cache-how-change-image-file-size-when-saving-cache-database-table
/// https://community.intersystems.com/post/how-resize-image-classmethod
/// 
Class Demo.REST.Server Extends %CSP.REST
{ Parameter CONVERTINPUTSTREAM = 1; Parameter CHARSET = "utf-8"; Parameter CONTENTTYPE = "application/json"; XData UrlMap
{
<Routes>
<Route Url="/api/v2/published/images/toBase64" Method="POST" Call="PostConvertImages"/>
</Routes>
} /// Example:
/// {
///    "Images": [
///        "D:\\PATH\1221701739_20190716134351965_1_0.jpg"
///    ],
///    "TempOutputDir": "D:\\PATH\\TEMP\\",
///    "UseResize": true,
///    "RemoveTempFiles": true,
///    "Dimensions":
///    {
///        "x": "640",
///        "y": "480"
///    }
/// }
/// 
ClassMethod PostConvertImages() As %Status
{
    set tSC=$$$OK
    try
    {
        //read input from REST    
        set sourceJson = {}.%FromJSON(%request.Content)         //setup response
        set tmpStream = ##class(%Stream.GlobalCharacter).%New()
        
        //initial output
        set targetJson = {}
        set targetJson.Images = []         //check tokens
        if (sourceJson.Dimensions.x'="")
        {
            set x = sourceJson.Dimensions.x
        }
        else
        {
            set x = "640"
        }         if (sourceJson.Dimensions.y'="")
        {
            set y = sourceJson.Dimensions.y
        }
        else
        {
            set y = "480"
        }
        
        set dimensions = x_"x"_y         if (sourceJson.TempOutputDir'="")
        {
            set tempDirectory = sourceJson.TempOutputDir
        }
        else
        {
            set tempDirectory = ""
        }         if (sourceJson.UseResize="")
        {
            set useResize = 0
        }
        else
        {
            set useResize = sourceJson.UseResize
        }         if (sourceJson.RemoveTempFiles="")
        {
            set removeTempFiles = 1
        }
        else
        {
            set removeTempFiles = sourceJson.RemoveTempFiles
        }         //Check for null
        if (sourceJson.Images '= "")
        {
            //setup image array 
            set imageArray = []             //iterate through images
            set iterArray = sourceJson.Images.%GetIterator()
            while iterArray.%GetNext(.key, .value)
            {
                try
                {
                    //get file details
                    set fileExtension = $PIECE(value,".",2) 
                    set imageFile = $REPLACE(value,"\\","\") //For escaped paths                     //get file size
                    set checkFile = ##class(%File).%New(imageFile)
                    set sc = checkFile.Open("R") 
                    $$$ThrowOnError(sc)
                    set fileSize = checkFile.SizeGet()
                    do checkFile.Close()                     if (fileSize > 0)
                    {
                        try
                        {
                            //work out what will be in base64 - if over certain number of bytes force resize regardless
                            if ((4 * fileSize / 3) > 300000)
                            {
                                set useResize = 1
                            }                             //check whether to rencode image
                            if ((useResize=1)&&(tempDirectory'=""))
                            {
                                //update flag
                                set tempFile = tempDirectory_$System.Util.CreateGUID()_fileExtension
                                set sc = $ZF(-100,"/STDIN="""_imageFile_""" /STDOUT="""_tempFile_"""","magick","fd:0","-resize",dimensions,"fd:1")
        
                                set convertedImageStream=##class(%Stream.FileBinary).%New()
                                set sc=convertedImageStream.LinkToFile(tempFile)                                 if ((sc = 1)&&(convertedImageStream.Size > 0))
                                {
                                    //encode to base64
                                    do ..Base64EncodeStream(convertedImageStream,.encodedStream)
                                    set line = ""
                                    do encodedStream.Rewind()
                                    while (encodedStream.AtEnd = 0) {
                                        set len = 5700
                                        set line = line_encodedStream.Read(.len)
                                    }
                                    set image = {}
                                    set image.src ="<img src=""data:image/"_$ZSTRIP(fileExtension,"*P")_";charset=utf-8;base64,"_line_""">"
                                }
                                else
                                {
                                    //create smallest jpg image 1x1
                                    set image = {}
                                    set image.src ="<img src=""data:image/jpg;charset=utf-8;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wgALCAABAAEBAREA/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxA="">"    
                                }
                                
                                //delete from disk
                                if (removeTempFiles=1)
                                {
                                    set deleteTmpFile = ##class(%File).%New(tempFile)
                                    set sc = deleteTmpFile.Delete(tempFile)
                                }
                            }
                            else
                            {
                                //encode to base64
                                set stream=##class(%Stream.FileBinary).%New()
                                set sc=stream.LinkToFile(imageFile)
                                do ..Base64EncodeStream(stream,.encodedStream)
                                set line = ""
                                do encodedStream.Rewind()
                                while (encodedStream.AtEnd = 0) {
                                    set len = 5700
                                    set line = line_encodedStream.Read(.len)
                                }
                                set image = {}
                                set image.src ="<img src=""data:image/"_$ZSTRIP(fileExtension,"*P")_";charset=utf-8;base64,"_line_""">"
                            }
                        }
                        catch ex
                        {
                            //ignore exception, create smallest jpg image 1x
                            set image = {}
                            set image.error = ex.DisplayString()
                            set image.src ="<img src=""data:image/jpg;charset=utf-8;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wgALCAABAAEBAREA/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxA="">"
                        }
                    }
                    else
                    {
                        //create smallest jpg image 1x1
                        set image = {}
                        set image.error = ex.DisplayString()
                        set image.src ="<img src=""data:image/jpg;charset=utf-8;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wgALCAABAAEBAREA/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxA="">"
                    }
                    do imageArray.%Push(image)
                }
                catch ex
                {
                    set image = {}
                    set image.error = ex.DisplayString()
                    do imageArray.%Push(image)
                }
                //Write images to string
                set targetJson.Images = imageArray
            }
        }           //write to stream
        do targetJson.%ToJSON(tmpStream)
        set ns = ##class(%Stream.GlobalCharacter).%New()
        do ns.CopyFrom(tmpStream)
        $$$ThrowOnError(tSC)
        set %response.ContentType = "application/json"
        set %response.Status = 200
        do ns.Rewind()
        return ns.OutputToDevice()
    }
    catch ex 
    {    
        set tSC = ex.AsStatus()
    }
    return tSC
} /// Encode a stream as BASE64, (based off intersystems exampk but with CRLF off)
ClassMethod Base64EncodeStream(pStream As %Stream, Output pEncoded As %Stream) As %Status
{
  s tSC=$$$OK
  try {
    s tSC=pStream.Rewind()
    q:$$$ISERR(tSC)     s pEncoded=##class(%Stream.TmpCharacter).%New()
    while 'pStream.AtEnd {
      s tLen=5700 
      s tSC=pEncoded.Write($system.Encryption.Base64Encode(pStream.Read(.tLen),1))
      q:$$$ISERR(tSC)
    }
    q:$$$ISERR(tSC)
        
    s tSC=pEncoded.Rewind()
  } catch (e) {
    s tSC=e.AsStatus()
  }
  q tSC
}

Thanks very much for replying with some suggestions. It's really helpful.

@Timothy Leavitt 
Thanks for the advice. I did try separate arguments in a previous version of my code, so it's nice to see I was on the right track originally so I will go back that way. With the $ZF(-100) approach I am getting 0k output file which suggest the STDOUT is being creating something (if somewhat unusable), so it could be something on the Windows side that is throwing the NOTOPEN.

@Danny Wijnschenk 
I've not thought of using OPEN. It's interesting option. I was exploring this briefly just now. What would you usually expect to see in the output normally. I've just been getting a 2.

Followers:
Daniel has no followers yet.
Following:
Daniel has not followed anybody yet.
Global Masters badges:
Daniel has no Global Masters badges yet.