Thursday, March 25, 2010

Action Scrip3 Bitmap to ByteArray to .Net C#

Previously, I posted on how I cropped and re-sized an image in Flex. Now, I will take you through some code snippets that I used to send the data over to .NET.

Flex:

I sent data to .NET using a UrlRequestLoader, again re-used code from some great person on the 'net.

public function saveFile(imageData:BitmapData):void{

  // method accepts raw JPEG data as ByteArray as input
  // create a URLRequest object pointed at the URL of the remote upload script
  var request:URLRequest = new   URLRequest(_uploadHandler+"?iKey="+_userId.toString());

  // the call we will make is a standard HTTP POST call
  request.method = "POST";

  // this enables us to send binary data for the body of the HTTP POST call
  request.contentType = "application/octet-stream";
  var urlLoader:URLLoader = new URLLoader();
  urlLoader.addEventListener(Event.COMPLETE,uploadPhotoHandler);

  // the loader is the data being sent along to the server. Its dataFormat   property lets us specify the format for the body, which, in our case,   will be BINARY data
  urlLoader.dataFormat = URLLoaderDataFormat.BINARY;

  // the data property of our URLRequest object is the actual data being   sent to the server, which in this case is the photo JPEG data
  var myEncoder:JPEGEncoder = new JPEGEncoder(100);
  var byteArray:ByteArray = myEncoder.encode(imageData);

  request.data = byteArray;
  urlLoader.load(request);

}

On the .NET side I had a handler setup to process this call. This part was easy to implement but took some digging to figure out all the right pieces.

It boiled down to getting the data out of the request using the BinaryReader. (actually I never knew that there was on attached to a Request). Then I used the File class to write out the image data.

.NET:
public void ProcessRequest(HttpContext context)
{

  if (context.Request.TotalBytes > 0)
    {

      int userId =       Convert.ToInt32(context.Request["iKey"]);
      byte[] data =       context.Request.BinaryRead(context.Request.TotalBytes);

      string basePath =ConfigurationManager.AppSettings["Path"];

      string uploadPath = context.Server.MapPath(basePath);

      if (!System.IO.Directory.Exists(uploadPath))
        System.IO.Directory.CreateDirectory(uploadPath);

      if(data!=null)
        {
        string fileName = uploadPath + "xxx.jpg"));
        File.WriteAllBytes(fileName, data);
        }
  }
}

ActionScript 3 - Crop and Resize Image

I needed to allow a user to select an image on their computer, crop that image down to whatever area they preferred, then save the image to the server. However, we wanted to limit the size of the image to 300 by 300. So if they uploaded an image of 1000 by 1000, the selected area needed to be reduced to a max of 300 by 300.

Okay, so the front end is Flash/Flex3, and the server end it C#.

The Flex part:
First to crop an image, I used the free tool called ImageCropper from FlexBlocks. This snazzy little tool made it very easy to crop the image, and then view the cropped image in an image component.

The drawback was that it did not seem to resize the underlying BitmapData like I needed. At least, I could not tell from any of the documentation that it would do this for me.

So I found this code on the 'net that did the most accurate job of resizing the image.

//this is the imagecropper working in this function
private function doCrop():void {

  // Get the cropped BitmapData
  var loadBD:BitmapData;
  loadBD = imageCropper.croppedBitmapData;

  var croppedBitmap:Bitmap;
  croppedBitmap = ResizeImage.resizeIt(loadBD, 300, 300) as Bitmap;

  //set preview image component's source to cropped bitmap
  croppedImage.source = croppedBitmap;

}


And here is the re-size function that someone wrote, I might have changed a few lines. Thanks to whoever wrote and shared this code...

public static function resizeIt(target:BitmapData, width:Number, height:Number):Bitmap{

  var ratio:Number = target.width/target.height;
  var targetHeight:Number = height;//now instead of setting the picture

  //size we calculate what the size should be with two new variables.
  var targetWidth:Number = targetHeight*ratio;
    if(targetWidth>width){
      targetWidth = width;
      targetHeight = targetWidth/ratio;
    }

  var bmp:BitmapData = new BitmapData(targetWidth, targetHeight);

  //create a bitmapData and pass it the size that the picture should be

  //create a matrix that we'll use to scale the picture
  var matrix:Matrix = new Matrix();

  //set the scale for the matrix
  matrix.scale((targetWidth/target.width), (targetHeight/target.height));

  //draw our bitmapData using our matrix, smooth the picture using true
  bmp.draw(target,matrix,null,null,null,true);


  var bitmap:Bitmap = new Bitmap(bmp);

  //create a bitmap and pass it our resized bitmapData
  bitmap.x = width/2-bitmap.width/2;//set the bitmap position
  bitmap.y = height/2-bitmap.height/2;//set the bitmap position

 return bitmap;//return the bitmap
}

When I get some time, I will post how I did the second part of this task which was sending the bitmap data over to .NET (C#), reading into a file and saving it to the server! So in the end, it is possible to do all this, even if I don't understand every last bit of it :)



Hope that helps.

Active Reports - Subreport data cut off

I call this experience a lesson learned. Active Reports warns of not touching controls in the Fetch_Data event, but I ignored it thinking a subreport isn't a control! And it caused me nothing but heartache to learn this lesson.

At first the report ran fine, I did not see the problem until I had alot more data to load, then I could see the subreport getting cut off. From looking at the report, it was clear the next report was being started, even though the first one's subreport data was not complete.

This is kind of how my report looked:

Library Name: My Library
Author Name: Joe Smith

Books
------------------------
My First Book
  Rating A: X
  Rating B: X
My Second Book
  Rating A: X
  Rating B: X
My Third Book
  Rating A: X
  Rating B: X
Total Rating: X

Page 1 of 3
**********************************

Library Name: My Library
Author Name: Pretend Name

Books
------------------------
My First Book
  Rating A: X
  Rating B: X
My Second Book
  Rating A: X
  Rating B: X
My Third Book
  Rating A: X <----DATA CUT OFF HERE!!!!


Page 2 of 3
**********************************

Library Name: My Library
Author Name: Ellen Jones

Books
------------------------
My First Book
  Rating A: X
  Rating B: X
My Second Book
  Rating A: X
  Rating B: X
My Third Book
  Rating A: X
  Rating B: X
Total Rating: X

Page 3 of 3
**********************************

This is sort of what I had in the code:


void report_FetchData(object sender, DataDynamics.ActiveReports.ActiveReport.FetchEventArgs eArgs)
{
  if(eArg.EOF==false)
   {
    int id = Convert.ToInt32(Fields["ID"].Value.ToString());

    //get some data
    List list = SomeCallToGetData(id);

    ReportDetail rd = new ReportDetail();
    this.subReport1.Report = rd;
    this.subReport1.Report.DataSource = list;
   }
}

The problem was that the Fetch_Data would spin through all the records, and this would not allow the subreport to be properly written out.

So to resolve my problem, I followed the instruction of "don't touch controls in the Fetch Data" and moved this logic to the Detail_Format - not sure if this the right way, but something lead me to believe this was the right....

Now, I use the Fetch_Data to gather all the data, then store the subreport data in a private class variable called '_list' for use in the Format_Detail like this:

public partial class SummaryReportAR6 : DataDynamics.ActiveReports.ActiveReport
{

  private List _list;

  public SummaryReportAR6 ()
   {
    InitializeComponent();
    this.FetchData += new FetchEventHandler(report_FetchData);
    this.detail.Format += new EventHandler(Detail_Format);
   }

  void report_FetchData(object sender, DataDynamics.ActiveReports.ActiveReport.FetchEventArgs eArgs)
  {
    if(eArg.EOF==false)
    {
     int id = Convert.ToInt32(Fields["ID"].Value.ToString());

     //get some data, store for later use
     _list = SomeCallToGetData(id);

   }
  }

  void Detail_Format(object sender, System.EventArgs e)
  {
     //set report datasource
     ReportDetail rd = new ReportDetail();
     this.subReport1.Report = rd;
     this.subReport1.Report.DataSource =      _list;
  }

}

hmmmm..I'm not really sure how the timing works out on this, but it seems to be working.

I will consider this one fixed until I see more problems.

Hope that helps someone out there.

Friday, March 5, 2010

Can't Access Localhost using IP Address or Computer Name

Well, I'm not sure when this feature on my pc broke, but it did and it WAS at some point working. I concluded that this can be a tricky little problem as I scoured the internet looking for an answer. Apparently, many people have this problem, and it is never really clear if they resolved it....or how they did.

I spent way too much time on this issue, but darn it I get really annoyed when something used to work, but then does not work anymore.

My problem was getting my locally served website to be accessed either by the ip address of my computer (http://xx.xx.xx.xx) or by the computer name (http://my-computer-name-here). On my computer, or another computer on the local network. When I would try these methods I got a page not found error. The same error occurred on other pc's too, make sense, but I had to hope...

I could access my site on my pc using the http://localhost. As a note, I'm on Vista but I don't think that was my problem. As a another note, I changed many things so I guess it could have been several of these steps combined, but I think it was the final steps that did it...

Okay so lets look at things that were right about the localhost, just in case you need to check this. My host file was correct. Located @ \windows\system32\drivers\ets\hosts

it contained the necessary line of:

127.0.0.1 localhost

My IIS was set up correctly, I had a Default Website running, IIS was running, the bindings on the default web site was just HTTP, port 80.

I had my Firewall OFF, I turned OFF my anti-virus. So that took those variables out of the equation.

I checked if my port 80 was open (-netsh -an), well actually, I tried to think I knew what I was looking at. Maybe subconsciously I did :)

I made sure I was not running Sql Server tools that might interfere like SSIS. (I read somewhere that may hog port 80 if on.)

I could ping my PC by computer name and ip address. (ping xxx.xxx.xxx)

I checked permissions on the physical folder, I made sure Anonymous access was on. I did not think it was an IIS problem with all these things in place.

So I finally found some post that sounded feasible, although not really directed at my problem. But, I decided to just blindly follow the instructions as a last straw. Totally risking they could be wrong...and in the end it did resolve my problem. Good thing. I was totally relieved.

In reflection, I faintly recall having to run this command before, but I forgot or someone else was running it for me. Gee thanks for babying me so I could spin my wheels for a whole day!

My steps, just as a reference, I'm not recommending anything to anyone....

turned off IIS

Open a command prompt
Type: netsh {enter}
Type: http {enter}
type: add iplisten ipaddress=xx.xx.xx.xx *message successfully added should follow
Type: exit
Open a command line
Type: ipconfig /flushdns

turned IIS back on.

browsed to http://localhost ...check...still working
browsed to http://xx.xx.xx.xx ..oh finally working
browsed to http://my-computer-name ...finally working

got on another pc, attempted remote access...finally working

okay, so I'm pretty sure adding the listener is what made this work, but maybe it was that flushing command. I was running XP before then 'upgraded' to Vista and changed my computer name so maybe it needed to clear something out. i'm not sure.

And again I don't know if this is right. All I know is that it made it work. So now I could go back to doing my original work...sending my localhost link to someone so they could look at a page design....

Hope that helps someone!