Using Windows PowerShell to download a file from a SAS Workspace session

In a previous article, I described how to use Windows PowerShell to connect to a remote SAS Workspace, run a SAS program, and retrieve the results. In that example, the only results I retrieved were the SAS log and SAS listing (text) output, if any.

When you run a SAS program on a remote session, sometimes the expected result is more substantial than just a text-based listing. What if your program creates Output Delivery System (ODS) files such as HTML or graphics? To retrieve those results, you need to download the files from the SAS session to your local machine, where your application can access them. Consider this program, implemented in a PowerShell script, which creates an image with PROC SGPLOT:

# change these to your own SAS-session-based
# file path and file name
# Note that $destImg can't be > 7 chars
$destPath = "/projects/results"
$destImg = "hist"

# program to run
# could be read from external file
$program =
"ods graphics / imagename='$destImg';
ods listing gpath='$destPath' style=plateau;
proc sgplot data=sashelp.cars;
histogram msrp;
density msrp;
run;"

# run the program
$objSAS.LanguageService.Submit($program);

When the program runs, it creates a file named hist.png in the /projects/results folder within the SAS session.

Downloading a file with FileService

The SAS Workspace provides the FileService API, which allows you to transfer file-based content between your local application and the SAS session. As with most file-based operations in SAS, the FileService relies on the use of a SAS fileref, or the name that SAS uses to reference your file within a program. For a file download operation, these are the basic steps:

Obtain a SAS reference to the file (FileService AssignFileref method.

Tell SAS to open the remote file for reading (OpenBinaryStream method).

Read the contents of the file into a local array of bytes (Read method, repeating in 1K increments).

Write the contents into a local file (PowerShell objects).

When completed, close the local and remote file handles, and unassign the SAS file reference (Close method and DeassignFileref method).

Here's a PowerShell program snippet that implements these steps. (The complete example is available on GitHub.)

# local directory for downloaded file
$localPath = "c:\temp"

# now download the image file
$fileref = ""

# assign a Fileref so we can use FileService from IOM
$objFile = $objSAS.FileService.AssignFileref(
"img", "DISK", "$destPath/$destImg.png",
"", [ref] $fileref);

$StreamOpenModeForReading = 1
$objStream = $objFile.OpenBinaryStream($StreamOpenModeForReading)

# define an array of bytes
[Byte[]] $bytes = 0x0

$endOfFile = $false
$byteCount = 0
$outStream = [System.IO.StreamWriter] "$localPath\$destImg.png"
do
{
# read bytes from source file, 1K at a time
$objStream.Read(1024, [ref]$bytes)

# write bytes to destination file
$outStream.Write($bytes)
# if less than requested bytes, we're at EOF
$endOfFile = $bytes.Length -lt 1024

# add to byte count for tally
$byteCount = $byteCount + $bytes.Length

} while (-not $endOfFile)

# close input and output files
$objStream.Close()
$outStream.Close()

# free the SAS fileref
$objSAS.FileService.DeassignFileref($objFile.FilerefName)

Write-Output "Downloaded $localPath\$destImg.png: SIZE = $byteCount bytes"

Here's an excerpt from the output when the script runs:

NOTE: PROCEDURE SGPLOT used (Total process time):
real time 13.18 seconds
cpu time 0.12 seconds

NOTE: Listing image output written to /projects/result/hist.png.
NOTE: There were 428 observations read from the data set SASHELP.CARS.

Downloaded c:\temp\hist.png: SIZE = 15092 bytes