Save images from Flash to your server using ASP.NET webservices

Problem:

You want to save an image from Flash to a server using Actionscript 3.0 and ASP.NET.

Solution:

Read this article!

Note: This blog is hosted on a Linux based server that uses PHP, therefore I can’t show you a working example directly on this blog (unless anyone with a Windows based hosting account would like to host one for me?). However, I can assure you this does work and you can download the files here and try it for yourself.

Download “FlashBitmapSave.zip”

Get this thing running

…or just take a look at the code/explanation…

Open the downloaded website in Visual Studio 2008 or Visual Web Developer Express 2008. 2005 versions should be fine as well.

Run it.

The default page should come up in your browser. You’ll see my example Flash app. Draw a picture and click “save to server” to – of course – save a JPEG version of your drawing from Flash.

Note: Saved JPEGs will be assigned a random GUID as a filename and saved directly to the root of your C: drive. You can modify the path in App_Code/Service.vb. If you need to know more about that aspect of it, I would continue reading the rest of this post.

If the flash app comes up but gives you the message “webservice failed to initialize…” then:

Click on the root of the project in your solution explorer.



In the properties panel below, take a look at your Virtual Path.



It should only have a “/” in it.



Try it again.

The process

  • Take a BitmapData object in Flash, and convert it to a ByteArray object using the JPGEncoder class (or, in my example, the Asynchronous JPEG Encoder class).
  • Take that ByteArray and Base64 encode it. We do this so that Actionscript and the .NET back-end will be speaking the same language. If we were using Flex and remoting software like WebORB, this type of conversion would likely happen automatically when sending or receiving a ByteArray object to/from Actionscript or .NET. But for this one-off scenario, doing it ourselves isn’t a big deal.
  • Use a third party AS3 webservice class to send the encoded ByteArray to our .NET webservice.
  • Our .NET webservice unencodes the ByteArray into its own form of ByteArray, creates a random file name using a GUID, and then saves the raw binary information to a .jpg file.

Some extra stuff you will need

  • Base64 encoder/decoder class
    This class from Dynamic Flash will be used to encode our ByteArray to Base64.
  • WebService component
    Adobe left the WebService classes out of Flash CS3. wellconsidered made their own. This is how we’ll be communicating with the server. This download is available as a .swc and needs to be dropped into your .fla to work.
  • Asynchronous JPEG Encoder
    In my example I use the Asynchronous JPEG Encoder. You could also use Adobe’s regular JPEG encode available in the AS3 CoreLib.

The Code

Now I’m just going to post the code. I did my best to comment the code thoroughly, but I’m not going to go through it line by line in this post. I’ll assume that if you’re in need of saving bitmap information from flash to a server using .NET, that most of what’s going on you already understand and maybe just needed an example to help you along. So here ya go!

The actionscript 3.0 code:

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.text.TextField;
        import flash.net.*;
        import flash.events.*;
        import flash.display.MovieClip;
	import flash.utils.ByteArray;
 
	// We'll be sending a ByteArray encoded as Base64, and then decoding it on the .NET side.
	// http://dynamicflash.com/goodies/base64/
	import com.dynamicflash.util.Base64;
 
	// be.wellconsidered classes give us an easy means to communicate with webservices.
	// The extension needs to be installed and the component then dropped into your .FLA.
	// You'll see a component called "Service" in the library of the example .FLA.
	// http://www.wellconsidered.be/blog/as3-webservice-component/
	import be.wellconsidered.services.WebService;
	import be.wellconsidered.services.Operation;
	import be.wellconsidered.services.events.OperationEvent;
	import be.wellconsidered.services.events.WebServiceEvent;
 
	// com.pfp includes classes for the Asynchronous JPEG encoder.  There is also a JPEG encoder
	// in Adobe's CoreLib (http://code.google.com/p/as3corelib/), but because this one is asynchronous it allows to 
	// monitor the rendering of the jpeg and therefore create a progress bar.  It is also essential when rendering larger
	// bitmaps, otherwise Flash could time out during the processing.
	import com.pfp.events.*;
	import com.pfp.utils.*;
 
    public class FlashBitmapSave extends MovieClip
    {
	// we create our WebService object.  Remember to include "?wsdl" in the URL.
	private var wsMainService:WebService = new WebService("/Service.asmx?wsdl");
 
                public function FlashBitmapSave()
                {
                        mcDrawingBoard.addEventListener(MouseEvent.MOUSE_DOWN, mcDrawingBoard_MouseDown);
			btnSaveToServer.addEventListener(MouseEvent.CLICK, btnSaveToServer_Click);
 
			// the listener on our webservice will let us know if there's a problem with the webservice,
			// if the url is wrong, if the service is down, etc.
			wsMainService.addEventListener(WebServiceEvent.INITFAILED, wsMainService_InitFailed);
                }
 
		// **** functions for the drawing pad **** //
 
		private function mcDrawingBoard_MouseDown(e:MouseEvent):void
		{
			mcDrawingBoard.addEventListener(MouseEvent.MOUSE_MOVE, mcDrawingBoard_MouseMove);
			stage.addEventListener(MouseEvent.MOUSE_UP, stage_MouseUp);
			mcDrawingBoard.graphics.moveTo(mcDrawingBoard.mouseX, mcDrawingBoard.mouseY);
		}
 
		private function mcDrawingBoard_MouseMove(e:MouseEvent):void
		{
			mcDrawingBoard.graphics.lineStyle(1, 0x000000);
			mcDrawingBoard.graphics.lineTo(mcDrawingBoard.mouseX, mcDrawingBoard.mouseY);
		}
 
		private function stage_MouseUp(e:MouseEvent):void
		{
			mcDrawingBoard.removeEventListener(MouseEvent.MOUSE_MOVE, mcDrawingBoard_MouseMove);
		}
 
		// ************************************** //
 
		private function btnSaveToServer_Click(e:MouseEvent):void
		{
			var bmdSnapshot:BitmapData = new BitmapData(mcDrawingBoard.width, mcDrawingBoard.height);
			// take a snapshot of what was drawn in "mcDrawingBoard"
			bmdSnapshot.draw(mcDrawingBoard);
 
			saveImage(bmdSnapshot);
		}
 
		private function wsMainService_InitFailed(e:WebServiceEvent):void
		{
			log("Webservice failed to initialize.");
		}
 
		private function saveImage(bmdSend:BitmapData):void
		{
			// create a new JPEGAsyncEncoder.  80 is the JPEG quality.
			var jpgEncoder:JPEGAsyncEncoder = new JPEGAsyncEncoder(80);
 
			// encodes 400 pixels before sending out a ProgressEvent.  That means every 400 pixels the CPU has a chance to "breath"
			// and process some other code.  
			jpgEncoder.PixelsPerIteration = 400;
 
			jpgEncoder.addEventListener(JPEGAsyncCompleteEvent.JPEGASYNC_COMPLETE, jpgEncoder_Complete);
			jpgEncoder.addEventListener(ProgressEvent.PROGRESS, jpgEncoder_Progress);
 
			jpgEncoder.encode(bmdSend);			
		}
 
		private function jpgEncoder_Complete(e:JPEGAsyncCompleteEvent):void
		{
			var jpgByteArray:ByteArray = new ByteArray();
			// put the raw image data we got back from the encoder into our ByteArray
			jpgByteArray = e.ImageData;
 
			sendToServer(jpgByteArray);
		}
 
		private function jpgEncoder_Progress(e:ProgressEvent):void
		{
			log("encoding... " + Math.round(e.bytesLoaded / e.bytesTotal * 100).toString());
		}
 
		private function sendToServer(byteArray:ByteArray):void
		{
			var opJPEGSaver:Operation = new Operation(wsMainService);
 
			// unfortunately, I don't think the WebService class we're using has a working ProgressEvent.
			// if I'm wrong, please let me know!
			opJPEGSaver.addEventListener(OperationEvent.COMPLETE, opJPEGSaver_Complete);
			opJPEGSaver.addEventListener(OperationEvent.FAILED, opJPEGSaver_Failed);	
 
			var strEncodedString:String = new String();
			// the Base64 class is static, so we can just call it using Base64.<whatever>
			strEncodedString = Base64.encodeByteArray(byteArray);
 
			// send the encoded bytes to our webservice function we called "SaveJPEG".
			opJPEGSaver.SaveJPEG(strEncodedString);
		}
 
		private function opJPEGSaver_Complete(e:OperationEvent):void
		{
			log("ByteArray successfully sent to server.");
		}
 
		private function opJPEGSaver_Failed(e:OperationEvent):void
		{
			log("Error sending ByteArray to server.");
		}
 
		private function log(strLog:String):void
		{
			// my own way of tracing to the textfield txtTrace -- as you can see.
			// not an important part of anything going on here.
			txtTrace.appendText(strLog + "\n");
			traceScrollBar.scrollTarget = txtTrace;
			txtTrace.scrollV = txtTrace.numLines;
		}
    }
}

The VB.NET code:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO
 
' To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
' <System.Web.Script.Services.ScriptService()> _
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Service
     Inherits System.Web.Services.WebService
 
 
    <WebMethod()> _
    Public Function SaveJPEG(ByVal strJPEG As String) As String
 
        'saves an uploaded photo and returns a unique name reference
 
        Dim BinaryContent As Byte() = Convert.FromBase64String(strJPEG)
 
        Try
            'generate a unique name (GUID) for the filename
            Dim strFileName As String = System.Guid.NewGuid.ToString("n")
            'MAKE SURE to change the save path!
            Dim fStream As New FileStream("C:\" + strFileName + ".jpg", FileMode.CreateNew)
            Dim bw As New BinaryWriter(fStream)
 
            bw.Write(BinaryContent)
            bw.Close()
            fStream.Close()
 
            'return the filename for use in Flash
            Return strFileName
 
        Catch ex As Exception
 
            Return ex.Message
 
        End Try
 
    End Function
 
End Class

31 Comments

  1. it is not working at all :(

    Comment by Ashish Kumar — October 2, 2008 @ 5:24 am

  2. Hey Ashish,

    Where are things going wrong? When you try and run the project from Visual Studio? Also, keep in mind that I have the flash movie set to send the image to server (your localhost in this case), but not re-download it. That is a very simple thing to add, and maybe I should have/will. Without any modifications you should be seeing your saved drawing as a JPEG directly on your C drive. Let me know, because I’d hate if everybody was having the same problem.

    Comment by rbosinger — October 2, 2008 @ 9:07 am

  3. Hey … It is perfect now..

    encoding… 101
    encoding… 102
    encoding… 100
    ByteArray successfully sent to server.

    I got saved image on C:\815df32f6d8740c9aebc1dcc33bb4fd9.jpg
    :) Thanks buddy..

    There was some mistake in configuring local host…

    Here one more thing i would like to ask..

    1. How Can i trace save image file name ???

    Comment by Ashish Kumar — October 2, 2008 @ 10:48 am

  4. Ashish,

    e.data will contain any string send back from the web server. So, in the case of my example you could trace e.data from the opJPEGSaver_Complete(e:OperationEvent) function. Now, keep in mind, this works because the C# code returns the filename (Return strFileName). e.data will contain whatever it returns.

    Comment by rbosinger — October 2, 2008 @ 11:11 am

  5. hey there, great piece of code,

    had to comment out the Line number 12
    //[Microsoft.VisualBasic.CompilerServices.DesignerGenerated()]
    line when i transformed it to c#

    and add
    using System;

    thank a lot!

    Comment by alexandros — October 23, 2008 @ 4:41 pm

  6. No problem Alexandros. Glad you managed to convert it to C#.

    Comment by rbosinger — October 24, 2008 @ 12:38 pm

  7. For the lazy plp :-p here is the c# code of the Services class.

    Don’t forget to update the Services.asmx to target the new c# class, and exclude your Services.vb or else the compiler will complain.

    using System;
    using System.IO;
    using System.Web.Services;

    [WebService(Namespace = "http://microsoft.com/webservices/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : WebService
    {
    ///
    /// Saves an uploaded photo and returns a unique name reference
    ///
    [WebMethod]
    public string SaveJPEG(string data)
    {
    byte[] image = Convert.FromBase64String(data);
    try
    {
    // Generate a unique name (GUID) for the filename
    string filename = Guid.NewGuid().ToString(“n”);
    // MAKE SURE to change the save path!
    FileStream s = new FileStream(“C:\\” + filename + “.jpg”, FileMode.CreateNew);
    BinaryWriter b = new BinaryWriter(s);
    b.Write(image);
    b.Close();
    s.Close();
    // Return the filename for use in Flash
    return filename;
    }
    catch (Exception e)
    {
    return e.ToString();
    }
    }
    }

    Comment by indyone — November 17, 2008 @ 8:09 am

  8. indyone,

    Thanks a lot for posting that. It will likely help someone out there!

    Comment by rbosinger — November 17, 2008 @ 10:22 am

  9. hi,
    i unzipped and opened a solution as u said..working fine…i added this app to the server another app which is running live(havethe virtual directory)..when i run this it throws error as web service falied…how to over come this…i got the same error when i run in local..but i solved what u suggested..how to solve in the server..
    Thanks in advance
    arun

    Comment by arun — November 19, 2008 @ 9:51 am

  10. Hi,

    Thank you , great code!
    I have an issue when I try to modify the flash project. When I publish, the control flickered and does not work.
    Any suggestions?
    I have adobe flash CS4.

    kib.

    Comment by kib — December 17, 2008 @ 3:45 pm

  11. Great thing
    and now comes the but…

    But is there a way to start with a template image ?

    Thanks for your answer

    Comment by flashlover — January 5, 2009 @ 12:34 pm

  12. flashlover:

    Sure thing. In the Actionscript code you’ll see that I’m basically taking a snapshot of whatever graphics lie in the “mcDrawingBoard” movieclip into a BitmapData object and then sending that data to the server. So if you were to include a template image in the “mcDrawingBoard” movieclip, and have the user draw over top of that, then the template image would get included in that snapshot as well. You could also directly insert images into the BitmapData object, or alter the image before encoding it and sending it out.

    Anything is possible, this is where your actionscript skills dictate what you can do.

    Comment by rbosinger — January 5, 2009 @ 1:37 pm

  13. Can someone point me to PHP version of this or similar script?

    Comment by Goran — January 12, 2009 @ 7:46 pm

  14. Hello Ryan,

    Thanks for sharing this great tutorial? Can I use it in a commercial project?

    Thank you very much for your responce.
    Kat

    Comment by Kat — January 15, 2009 @ 8:16 am

  15. Ryan, you are my new best friend. This works perfectly, even with me changing bits and pieces for my own needs as I go.

    I’m experimenting with using a geom.Rectangle to clip the image before uploading to the server, I will post results here if I have any success that other readers might be interested in.

    Again, thanks for this excellent post dude!

    Comment by Ben Parsons — March 16, 2009 @ 4:37 pm

  16. Hi
    Thanks to whom ever uploaded the C# code!

    One point to note when opening a website in VStudio is to ensure you select file path on the lft hand side…

    It works great…

    When i try to recompile flash with no chnages it throuws the following output and complier error…

    any ideas

    USING CS4

    ReferenceError: Error #1065: Variable ComponentShim is not defined

    5000: The class ‘be.wellconsidered.services.Operation’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

    5000: The class ‘be.wellconsidered.services.events.OperationEvent’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

    5000: The class ‘be.wellconsidered.services.WebService’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

    Comment by Geoff — April 28, 2009 @ 11:46 am

  17. ohh and thanks obviously to the guy who wrote it!!!!

    Comment by Geoff — April 28, 2009 @ 11:48 am

  18. found the answer if yuou compile in CS3 it compiles fine, but in CS4 it doesnt…

    thats the answer not the solution… will let you all know ehn i hvae this…

    Comment by Geoff — April 28, 2009 @ 12:04 pm

  19. Hey thaks for the help. that is nice explation.

    Comment by Ashish — May 5, 2009 @ 7:39 am

  20. How do I update the VB code to save jpeg files without a randomly generated file name but either an incremented filename or a filename with the date and time stamp on it or all of the above?

    For instance:
    image001.jpg
    image002.jpg

    or

    image10_15_09-16_42.jpg (later part is military time 16:42)
    image10_16_09-12_23.jpg

    or something even close to any of these examples I just need a incremented name so reviewers of the file know what order they were saved in.

    Comment by Sam I Am — October 15, 2009 @ 4:45 pm

  21. Is there a way to create a progress bar for the bytes uploaded using this aproach?

    Comment by mat — November 26, 2009 @ 11:37 am

  22. Anyone ever discover why this will not compile in CS4? Im working on an app that uses CS4′s 3D effects and therefore cannot use CS3.

    Comment by James — December 2, 2009 @ 10:43 am

  23. here is the error thrown in CS4:
    ReferenceError: Error #1065: Variable ComponentShim is not defined.

    Comment by James — December 2, 2009 @ 10:44 am

  24. I suggest using the full source of the webservice component you can download (using svn) from http://code.google.com/p/as3webservice/

    Examples works great by the way!

    Comment by Tom — December 4, 2009 @ 9:54 am

  25. This is absolutely amazing! Thanks so much. How would I name the image using a variable from the ASPX page? I would love to be able to name this photo using an ID or something like that.

    Comment by Casey — February 19, 2010 @ 9:43 am

  26. if I need inmediately send the file to the client for download, I should do?

    Comment by edwin — May 5, 2010 @ 12:01 pm

  27. Hi Ryan
    Thanks for nice solution.
    Can you tell me how can i open saved jpg in edit mode in ASP.Net in Flash ??
    I am working on same application which require to save image from Flash to FTP server.
    Like Paint i can create one image on which i can draw rectangle,put image,rotate image,scale image etc.
    Now i want to save this image.You have already answered my this question.
    But now i want to open this file in edit mode so i can able edit image.
    Any help ?
    I hope you understand my question.

    Thanks
    Pushkar

    Comment by Pushkar — April 5, 2011 @ 8:41 am

  28. I discovered your blog site on google and check a few of your early posts. Continue to keep up the very good operate. I just additional up your RSS feed to my MSN News Reader. Seeking forward to reading more from you later on!鈥?

    Comment by funny product name translations — April 7, 2011 @ 11:16 pm

  29. Thanks for saving my but, potentially… I’m having trouble importing the classes to my clip the –
    import be.wellconsidered.services.WebService;
    import be.wellconsidered.services.Operation;
    import be.wellconsidered.services.events.OperationEvent;
    import be.wellconsidered.services.events.WebServiceEvent;
    - to be more specific.

    Where can I get the original WebService component? I think that’s what maybe causing it. Otherwise great great code!

    Comment by Peter — May 25, 2011 @ 3:05 pm

  30. Ignore previouse comment! I got it to work, you are a GOD! For all I care you can have my first born child! I cannot thank you enough for the code you shared. I had one week to learn AS3 and make a flyer that gets saved on the server, without this code I would have been in huge trouble!

    Comment by Peter — May 26, 2011 @ 3:28 am

  31. Hello
    thanks for great article i am working on flash cs5 i am facing some error related to ComponentShim, be.wellconsidered.services… does anybody tried this flash cs5

    Thanks
    Arjun

    Comment by arjun — August 9, 2011 @ 2:45 pm

Sorry, the comment form is closed at this time.