Question
Andy Stobirski · May 18

Downloading files from ZEN pages

Hi all

I have a Zen page which displays the contents of a table. One of the columns is a file path to a file on a local disk.

Is it possible, on a Zen page, to allow that file that described by the file path to be downloaded when a hyperlink is clicked on?

Cheers

Product version: IRIS 2021.1
$ZV: IRIS for Windows (x86-64) 2021.2.1 (Build 654U) Fri Mar 18 2022 06:09:35 EDT
0
1 129
Discussion (7)3
Log in or sign up to continue

In the past, I have utilized %CSP.StreamServer to accomplish this.  While this may have a %CSP package name its perfectly usable in a ZEN context.

If the file isn't accessible to link to directly, you may want to look into extending the %CSP.StreamServer class and linking to that. At a bare minimum, you'll want to override the OnPage and OnPreHTTP methods:

ClassMethod OnPage() As %Status
{
set myfile = ##class(%File).%New("/path/to/file")
do myfile.Open("S")
do myfile.OutputToDevice()
quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ Language = cache ]
{
do %response.SetHeader("Content-Disposition","attachment;filename=""filename""")
quit 1
}

Of course using your own file name and the path to the file. That's the local computer file path, not a URL. You also should set the content type appropriately using set %response.ContentType = "text/csv" or whatever the MIME type of the file is so that the browser can identify it correctly.

Unless you want to have to write another %CSP.StreamServer for every file, you'll have to pass the name of the filepath as an argument. So that would look more like:

ClassMethod OnPage() As %Status
{
set myfile = ##class(%File).%New(%request.Get("filepath"))
do myfile.Open("S")
do myfile.OutputToDevice()
quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ Language = cache ]
{
set filepath = %request.Get("filepath")
set %response.ContentType = "text/csv" //or whatever the appropriate MIME type is
do %response.SetHeader("Content-Disposition","attachment;filename="""_$P(filepath,"/",*)_"""")
quit 1
}

Then you could link to it as whatever the path to your stream server is with ?filepath=/path/to/file on the end.

If you take that approach, though, do some validation on the filepath and make sure it can ONLY go to the folder you want! Or, only pass the filename as a parameter to the page, and hard-code the folder in those methods.

Hi there! Thanks for taking the time to reply. I implemented the above code and was quickly able to download the file in question. However, the PDF files that is downloaded doesn't have the same size as the original - in fact all are 5Kb smaller, e.g.197Kb downloads to a 192Kb file, 241Kb to a 236Kb etc

I modified the OnPreHttp() method to set content length, but that had no effect.

/// Set the appropriate header for the file.
ClassMethod OnPreHTTP() As %Boolean
{
set filepath = %request.Get("filepath") set tFs=##class(%Stream.FileCharacter).%New()
set tFs.Filename=filepath set %response.ContentType = "application/pdf"
do %response.SetHeader("Content-Disposition","attachment;filename="""_$P(filepath,"\",*)_"""")
Do %response.SetHeader("Content-Length",tFs.Size)     Quit $$$OK
}
 

Do you have any ideas?

I solved it.

I changed to OnPage() method to use a %Stream.FileCharacter as follows:

ClassMethod OnPage() As %Status
{
	
	set fs = ##class(%Stream.FileCharacter).%New()
	set fs.Filename = %request.Get("filepath")
	
	do fs.OutputToDevice()
	quit $$$OK
}

For the record, actually having a page with the above allows anyone with access to the page to get access to arbitrary files on the server. (see my comment below from May 18)

You need to either do very strict input validation on filepath, or (if the full path is known in the database) use %CSP.StreamServer properly with an encrypted path.

WARNING! When you implement this (using %CSP.StreamServer or whatever), make sure that you're not opening up a way for a malicious user to download any arbitrary file on the server.

Thanks for the warning. Fortunately, this is for a internally hosted page accessed by username/password only by authorised operatives.