New technique for file downloads - using Base64

Hi,
There are several posts discussing techniques for file downloads. We wanted to offer the following technique, mostly so the community can poke holes in it and improve it (!) and also in case it’s of value to anyone here.

Our solution is for a medical practice and file downloads contain protected health information. We were leary about using anything that requires files to be exposed on the public Internet, either through our server, or in S3, etc., or for files to be stored in a temp location on our server without total control over them. The data api method of getting a URL seems interesting but we wanted to wait until that’s more tested, and I believe it relies on temporarily storing files in a Public location.

So what we’re doing is this:

(1) An authenticated user clicks a button on a form and that fires a standard utility hook

(2) Inside the hook:

(a) Go to the user’s record, where we have the PDF file stored as a base64 string in a text field (Users::base64ofPDF). When you generate the base64 string, do this: Substitute(Base64Encode ( Users::containerOfPDF ); Char(13)&Char(10); “”).

(b) Insert text with target $function:

let data64 = ‘d64’;
let a = document.createElement(“a”);
a.href = “data:application/octet-stream;base64,”+data64;
a.download = “Results.pdf”
a.click();

© Set variable $function

Substitute($function; “d64”; Users::base64ofPDF)

(d) Set variable $$BF_Actions

BF_SetAction_function ( $function )

That’s it - when the hook completes, the function will run and download a file “Results.pdf”. With a 1pg PDF, the script execution time is ~200ms. With a 20pg PDF, it’s 300-400ms but further testing is in order. PDFs shouldn’t contain large hi res graphics.

Very interested to know if anyone sees anything wrong with this or any way to improve?

1 Like

FMS V19 has a new executeDataAPI script step which you can run to return your container layout.

The returned data will have a url for the container that can be used within 15 minutes for file download.

You can’t used this for in app <img> tags as there are CORS headers preventing it. ( alternately you could use a proxy servers perhaps.

This works pretty good for file downloads though.

I have been experimenting with this and it looks promising, although forcing the file to download rather than display in the browser does not appear to be straightforward.

I would not be surprised if FM gives greater control in future version.

Has there been any update on this? Being able to easily download container files would be awesome.

Very helpful - thanks for this. I am implementing this technique and it appears to work well.

One minor addition I added was to open the PDF in a new tab in an iFrame. Our users are often very poor with computers so this prevents some support phone calls.

So this function will both download the file as well as open it in a new browser tab.

let data64 = 'd64';
let a = document.createElement("a");
let pdfWindow = window.open("");
a.href = "data:application/octet-stream;base64,"+data64;
a.download = "Results.pdf";
a.click();
pdfWindow.document.write(
    "<iframe width='100%' height='100%' src='data:application/pdf;base64, " +
    encodeURI(data64) + "'></iframe>"
);