search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Silverlight: Handling Cross-Domain Images and Gifs


By Peter Bromberg
Printer Friendly Version
View My Articles
44 Views
    

An easy way to get around cross-domain image urls and handle gif images for Silverlight.


In my travels working with Wally McClure and  David Silverlight  on the Silverlight 3 UI client for Wally's TimedTweet Azure service, I discovered that when we bring back a Twitter Timeline, all the users' profile images point to Amazon S3 storage.  There is no crossdomain.xml or clientaccesspolicy.xml file there, so all the image url requests will be denied by  Silverlight. Obviously, for better or worse, Silverlight has been trained to know that any image that doesn't come from the same domain it was served from is a baddie, unless there is an enabling policy file there. Now of course all this extra checking means extra HTTP Requests over the wire, but hey - that's progress.

Tim Heuer and others have some fancy workarounds involving DNS server settings, but I think the easiest solution for me is the simplest one. Just make a "ClientAccess.ashx" handler on the same server / domain that that Silverlight app is served from , and convert  all the Amazon image urls to point to ClientAccess.ashx?url=amazonimageurl - with the original Amazon image url on the querystring. Your handler will then read the "url" item from the Request.QueryString collection, make the request for the image, and Response.BinaryWrite the image bytes out to the requesting Silverlight client. Since Silverlight sees this as a same-domain request, there are no security issues and your images will come back just fine.

There is an additional benefit of using this technique: Silverlight - even Silverlight 3 -- cannot display gif images natively.  Some (but not all) of the Twitter user Profile images are indeed gifs. There is some code out in the wild that will decode gif images in Silverlight, but since I am already serving my images from my server with the ASHX handler, it just makes sense to convert gifs to jpegs there too.   So all we need to do is have the ASHX handler check to see if ".gif" is in the url it's been given, and if so, we can create a .NET Bitmap object from the image bytes, convert it to a jpeg image, and rewrite out the new jpg byte array and serve it out. Silverlight will now happily consume the converted jpeg image and we've solved all our problems in one fell swoop. This approach to handling the delivery of off-domain images will work equally well in Silverlight 2.

Here is an example of how this all works; for this "demo" I'm just doing it with a single gif image from Amazon:

public MainPage()
        {
            InitializeComponent();
            // test Amazon s3 url to a Twitter User's Profile Image that is a gif:
            string amazonImageUrl =
                "http://s3.amazonaws.com/twitter_production/profile_images/53709078/me2_bigger.gif";
            string imgUrl =Application.Current.Host.Source.Scheme + "://" + Application.Current.Host.Source.Host + ":" +
                Application.Current.Host.Source.Port + "/ClientAccess.ashx?url=" + amazonImageUrl;
           image1.Source = new BitmapImage(new Uri(imgUrl, UriKind.RelativeOrAbsolute));

           
        }
Here is the code for my ASHX handler:
using System;
using System.Collections;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Net;

namespace SLImageHandler.Web
{
   
   
    public class ClientAccess : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            string imageUrl = context.Request["url"].ToString();
            WebClient client = new WebClient();
            byte[] bytes = client.DownloadData(new Uri(imageUrl));

            // convert gif to jpg  here
            if(imageUrl.Contains(".gif"))
            {
                Bitmap bmp = (Bitmap)Bitmap.FromStream(new MemoryStream(bytes));
                bytes = GifConverter.ConvertGif(bmp);
            }
            context.Response.BinaryWrite(bytes);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
And finally, here is the code for my little "GifConverter" utility:
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Web;

namespace SLImageHandler.Web
{
    public class GifConverter
    {
        public static byte[] ConvertGif(Bitmap Image)
        {

            MemoryStream objStream = new MemoryStream();
            ImageCodecInfo objImageCodecInfo = GetEncoderInfo("image/jpeg");
            EncoderParameters objEncoderParameters;
            try
            {
                if (Image == null)
                    throw new Exception("ImageObject is not initialized.");
                objEncoderParameters = new EncoderParameters(3);
                objEncoderParameters.Param[0] = new EncoderParameter(Encoder.Compression,
                (long)EncoderValue.CompressionLZW);
                objEncoderParameters.Param[1] = new EncoderParameter(Encoder.Quality, 100L);
                objEncoderParameters.Param[2] = new EncoderParameter(Encoder.ColorDepth, 24L);
                Image.Save(objStream, objImageCodecInfo, objEncoderParameters);
            }
            catch 
            {
                throw;
            }
            return objStream.ToArray();
        }

        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }
    }
}
You can download the Visual Studio 2008 Silverlight 3 Beta solution here.

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
Please post questions at forums, not via email!

button
Article Discussion: Silverlight: Handling Cross-Domain Images and Gifs
Peter Bromberg posted at Friday, May 15, 2009 12:41 PM
Original Article