1

I'm building a pipeline component that can convert an image format to another format (say jpg to png for example):

using (System.Drawing.Bitmap bmpSource = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(msgReceived.BodyPart.Data))
{
    VirtualStream strConvertedImage = new VirtualStream();

    bmpSource.Save(strConvertedImage, System.Drawing.Imaging.ImageFormat.Png);

    strConvertedImage.Flush();
    strConvertedImage.Position = 0;

    ctxPipeline.ResourceTracker.AddResource(strConvertedImage);
}

msgReceived.BodyPart.Data = strConvertedImage;

I use this component in a receive pipeline with a file adaptor, and it executes fine, but when I subscribe with a passthrough send port and file adaptor, the file written is only part of the actual image.

Note:

I can use this same code (with the exception of loading the source bitmap from a file stream instead a BizTalk message stream) in a windows console application and it works fine, so this seems BizTalk related.

I think it has something to do with the source stream.

It seems odd behavior, but if I inspect the length and position properties of msgReceived.BodyPart.Data, Length is 904678. Position is 0. Position stays at 0 and never changes even as the bitmap is loaded and saved. I would have expected that as I load the bitmap, the position changes, unless the BitMap.FromStream method seeks to the beginning of the stream once it's finished loading.

Also, When I save the bitmap to my new stream as a png, the new stream length is always 54789, representing only the first party of the image. Now if I immediately save the same bitmap object to a new stream again, it's size is then 1400868, representing the entire image and msgReceived.BodyPart.Data.Position is now 904678 indicating that the entire source stream has been read.

Here's an example:

using (System.Drawing.Bitmap bmpSource = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(msgReceived.BodyPart.Data))
{
    VirtualStream strConvertedImage = new VirtualStream();
    bmpSource.Save(strConvertedImage, System.Drawing.Imaging.ImageFormat.Png);
    strConvertedImage.Flush();
    strConvertedImage.Position = 0;

    //msgReceived.BodyPart.Data.Position is 0
    //msgReceived.BodyPart.Data.Length is 904678
    //strConvertedImage.Position is 54789 (Only part of image was saved)

    strConvertedImage = new VirtualStream();
    bmpSource.Save(strConvertedImage, System.Drawing.Imaging.ImageFormat.Png);

    //msgReceived.BodyPart.Data.Position is 904678
    //msgReceived.BodyPart.Data.Length is 904678
    //strConvertedImage.Position is 1400868 (full image was saved)

    msgReceived.BodyPart.Data = strConvertedImage;
    ctxPipeline.ResourceTracker.AddResource(strConvertedImage);
}

Why would the position of msgReceived.BodyPart.Data only advance on the second save? And why would the first save attempt only save part of the converted image. I'm baffled!

Jeremy
  • 44,950
  • 68
  • 206
  • 332

1 Answers1

1

I'd avoid using the Data property except to set the stream at the end; per MSDN it clones the data stream and sometimes gives unexpected behavior (I've seen the Data property be null in some adapters even though GetOriginalDataStream() successfully returned the stream). Use GetOriginalDataStream() instead. I'd also avoid using using blocks in a pipeline - while I can't see why your example would be a problem, it gets very easy to mistakenly dispose of a stream that ends up disposing of the original data stream.

For example,

Stream originalStream = msgReceived.BodyPart.GetOriginalDataStream();
originalStream.Seek(0, SeekOrigin.Begin);
VirtualStream vtsConvertedImage = new VirtualStream()
System.Drawing.Bitmap bmpSource = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromStream(originalStream);

bmpSource.Save(vtsConvertedImage, System.Drawing.Imaging.ImageFormat.Png);

vtsConvertedImage.Flush();
vts.ConvertedImage.Seek(0, SeekOrigin.Begin);

msgReceived.BodyPart.Data = vtsConvertedImage;
ctxPipeline.ResourceTracker.AddResource(vtsConvertedImage);
ctxPipeline.ResourceTracker.AddResource(bmpSource);
Dan Field
  • 20,885
  • 5
  • 55
  • 71
  • That's really good advice, and answers a question I've had regarding the Data property vs GetOriginalDataStream(). Unfortunately I've already tried both and it doesn't have an impact. – Jeremy May 15 '15 at 17:00
  • Only other thing I can think of is wrapping the OriginalDataStream in a ReadOnlySeekableStream, or another VirtualStream, and loading it from that... – Dan Field May 15 '15 at 17:57