A while back when Adobe released version 10 of their Flash plugin, many many actionscript programmers freaked out when they could no longer user the FileReference class to automatically open a save dialog. In version 10, Adobe put in place “security” changes that required a user to explicitly click in order for the FileReference class to do its thing. (see Understanding the security changes in Flash Player 10)
Well today I found a work-around that none of my peers had seen before, so I’d like to share it with you.
The Content-disposition Header
Have you ever wondered how some websites are able to tell a browser to “save” a file (open up a save dialog) rather than to display a file? One way to do this is to send a special header from the server called “Content-disposition.” This is the key to getting around opening a save dialog without the user clicking. To read more about Content-disposition, read this nice article from Microsoft – How to raise a “File Download” Dialog Box for a Known MIME Type.
How to get around the limitation of Flash 10
So here’s the idea. In your actionscript code, you already have the name of the file you want to pop open a dialog for the user to save. Normally (in Flash 10 and beyond) you would need the user to click first, and then you would use the FileReference class to pop open a dialog. But we don’t want to require the user to click. Rather, we will tell javascript to navigate the page to a script that send Content-disposition: attachment. Since it is an attachment, the browser doesn’t navigate away from the flash site. Instead, it pops open a dialog window to save the file that is sent back from the script.
The Actionscript
public function DownloadFile(fileURL:String):void{
ExternalInterface.call("window.location=\"download.ashx?fileName=" + fileURL + "\"");
}
This function accepts the name of the file you want to download and sends a simple line of javascript via external interface. No big deal.
The Server-Side Script
I’ve been a big fan of generic HttpHandlers lately, so that is what I used. You could just as easily use php, ruby, or normal asp.net for this. The main thing you need to do is set the Content-disposition header, and then send the file you need as the response. The following is the content for the file called download.ashx:
<%@ WebHandler Language="C#" Class="download" %>
using System;
using System.Web;
using System.IO;
public class download : IHttpHandler {
public void ProcessRequest (HttpContext context) {
// grab the name of the file we need to send out
string filename = context.Request.Params["fileName"];
// read the file into a byte array
byte[] bytes = File.ReadAllBytes(context.Server.MapPath("files/" + filename));
// clear the headers
context.Response.ClearHeaders();
// clear ouat the response
context.Response.Clear();
// set the Content-disposition to attachment and
context.Response.AddHeader("Content-disposition", "attachment; filename=" + filename);
// set the content type - might not be image/jpeg for you
context.Response.ContentType = "image/jpeg";
// send the content out
context.Response.BinaryWrite(bytes);
// end the response
context.Response.End();
}
public bool IsReusable {
get {
return false;
}
}
}
And The Point Is
The point here is that you can use javascript to call a server-side script that sets the Content-disposition header to attachment. This in effect produces a save-dialog window similar, if not identical to the dialog that gets opened by the FileReference class in actionscript.
Furthermore, you can call this simple javascript line from within actionscript using ExternalInterface, and this call does not require user interaction.
I hope this helps!