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

Some extra stuff you will need

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

Comments

9 Responses to “Save images from Flash to your server using ASP.NET webservices”

  1. Ashish Kumar on October 2nd, 2008 5:24 am

    it is not working at all :(

  2. rbosinger on October 2nd, 2008 9:07 am

    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.

  3. Ashish Kumar on October 2nd, 2008 10:48 am

    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 ???

  4. rbosinger on October 2nd, 2008 11:11 am

    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.

  5. alexandros on October 23rd, 2008 4:41 pm

    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!

  6. rbosinger on October 24th, 2008 12:38 pm

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

  7. indyone on November 17th, 2008 8:09 am

    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();
    }
    }
    }

  8. rbosinger on November 17th, 2008 10:22 am

    indyone,

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

  9. arun on November 19th, 2008 9:51 am

    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

Leave a Reply