Symmetri Developer Blog

April 29, 2009

Download Flex UI Component as JPG

Flash/Flex - By Shourov Bhattacharya

So I finally got this working. What I wanted to do was to add a "Save As Image" option to charts and grids which are part of a client’s reporting suite that is being developed as an online Flex application; the idea being that this would work just like it does with images in a browser’s HTML pages, allowing the user to save an image to the desktop or anywhere else on the local filesystem.

Getting the image data is easy - the new ImageSnapshot class in Flex 3 allows you to easily take a Bitmap "snapshot" of any DisplayObject and keep it as a ByteArray. However, getting the captured image back to the user is a little trickier. Flash Player 9 and below has a security sandbox model that disallows your Flex application from sending a file straight back through the browser, so what we have to do is "reflect" the image data off a server page - in other words, get our user’s browser to handle a new request that uploads the image data back to the server and then downloads that exact same data again, with the right headers, back in the browser client.

On the Flex side, I wrote up a little class which handles all the mechanics of taking the snapshot and launching the "reflected" page:

	
package com.symmetri.Tools
{
    import flash.display.IBitmapDrawable;
    import flash.events.*;
    import flash.net.*;
    import flash.net.navigateToURL;
    import flash.utils.ByteArray;
	
    import mx.controls.*;
    import mx.core.*;
    import mx.graphics.ImageSnapshot;
    import mx.graphics.codec.JPEGEncoder;
    import mx.utils.*;    
	
    public class ImageExporter
    {
        private var _reflectUrl:String;
	
        private var _snapshot:ImageSnapshot;
        private var _rawData:ByteArray;
	
        public function ImageExporter(reflectUrl:String)
        {
            _reflectUrl = reflectUrl;
        }
	
        // take a snapshot of a UI component as a JPG image
        // pass dpi of zero to take it at native screen resolution
        public function snapshotObjectAsJPGByteArray(source:IBitmapDrawable, dpi:Number = 0):ByteArray
        {
            // take the snapshot
            _snapshot = ImageSnapshot.captureImage(source, dpi, new JPEGEncoder());
            return (_snapshot.data as ByteArray);
	
        }
	
        // take a snapshot of a UI component as a JPG image
        // and send it to the browser as a download file
        // pass dpi of zero to take it at native screen resolution
        public function downloadObjectAsJPG(source:IBitmapDrawable, dpi:Number = 0):void
        {
            _rawData = this.snapshotObjectAsJPGByteArray(source, dpi);
	
            var _request:URLRequest = new URLRequest(_reflectUrl);
            _request.method = URLRequestMethod.POST;
            _request.contentType = application/octet-stream;
            _request.data = _rawData;
	
            navigateToURL(_request, _self);
        }
    }
}

On the server side, we have a blank ASPX page with the following code-behind (C#):

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.IO;
	
public partial class ReflectJPG : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // method “attachment” or “inline”
        string method = (Request.QueryString[method] == null ? attachment: Request.QueryString[method].ToString());
	
        // filename
        string filename = (Request.QueryString[filename] == null ? image.jpg : Request.QueryString[filename].ToString());
	
        byte[] data = readPostedFile();
	
        Response.ContentType = image/jpg;
        Response.AddHeader(Content-Length, data.Length.ToString());
        Response.AddHeader(Content-disposition, method + ; filename= + filename);
        Response.BinaryWrite(data);
        Response.End();
    }
	
    // read the posted file
    private byte[] readPostedFile()
    {
        if (Request.ContentLength > 0)
        {
            byte[] buffer = new byte[Request.ContentLength];
            using (BinaryReader br = new BinaryReader(Request.InputStream))
                br.Read(buffer, 0, buffer.Length);
            return buffer;
        }
        else
        {
            return null;
        }
    }
}

Finally, to use the ImageExporter class in Flex, I use the following pattern to download a JPG snapshot of the component viewReport:

var exporter:ImageExporter = new ImageExporter(”http://www.symmetri.com/ReflectJPG.aspx”);
    exporter.downloadObjectAsJPG(viewReport);

1 Comment »

  1. That’s great, I never thought about Symmetri Developer Blog like that before.

    Comment by Yacht Charter GreeceĀ  — October 3, 2009 @ 6:31 am

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>



Anti-spam measure: please retype the above text into the box provided.

Get free blog up and running in minutes with Blogsome
Theme designed by Janis Joseph