Binary data with NAV Web Service

4 Apr

A few weeks ago, a customer asked me to create a web service that was able to import data from a web form into Microsoft Dynamics NAV. Since they are using NAV 2009 R2, this didn’t sound very challenging. Just one small detail triggered me: the web form also contained a field to upload a picture. This picture should also be imported into NAV.

To not reinvent the wheel, I started a quick search on the internet to found out if there is information about importing binary data into NAV using a web service. But the weird thing was that I found enough information about exporting binary data, but barely any information about the importing side.

Another point about the examples with binary data is that they were using a temporary file. And I always try to avoid temporary files. In my opinion, it is much better to use memory.

Base64 encoding

To exchange binary data in a web service, it is common use to convert the binary data to Base64 encoding. The .Net Framework has built-in support for Base64 converting with the System.Convert class.

To convert a Base64 string to binary data, we can use the Convert.FromBase64String method. It accepts a string as input parameter and returns a converted byte-array.

To convert binary data to a Base64 string, we can use the Convert.ToBase64String method. It accepts a byte-array and returns a Base64 encoded string.

Basically, a BLOB field in NAV contains a byte-array, so the challenge is how to read or write this byte-array and use it in the conversion methods. In .Net we can use Stream objects to work with byte-arrays.

Stream objects

In Microsoft Dynamics NAV, BLOB fields offer a possibility to read and write data with Stream objects. For reading data from a BLOB, we can use the InStream object and for writing data we use the OutStream object. However, these NAV Stream objects don’t have methods to read the data as a .Net byte-array.

But, we have .Net Interoperability! The NAV Stream objects are exchangeable with the .NET System.IO.Stream class. For a perfect explanation if this, read this post from NAV MVP Vjekoslav Babic. I’m not going to repeat his explanation, this post is to explain another possibility to work with streams.

Now we are getting closer. The solution is to find a .Net Stream object that lives in memory and is able to read and write byte-arrays. The .Net System.IO.MemoryStream class, is a class that is based on the System.IO.Stream class. And like the name already explains: it operates completely in memory.

Web service Codeunit

Here is an (simple and short) example of a NAV web service with to methods: SetItemPicture and GetItemPicture.

The SetItemPicture takes a Base64 encoded string, converts it to binary data and uses a MemoryStream object to write it to the OutStream of BLOB field.

The GetItemPicture uses a MemoryStream object to read the byte-array from a InStream object. Then it converts the byte-array to a Base64 encoded string and returns it in a BigText variable.

(Did you know that the BigText variable in a web service behaves like a normal string? Without the 1024 character limit.)

image

The variables of this SetItemPicture method. (I guess you can imagine the variables of the GetItemPicture method.)

image

 

Testing the web service

Here is an example to test the binary import / export web service.

using System;
using Demo_Import_Item_Picture.ImportItemDataService;
using System.IO;

class Program
{
  static void Main(string[] args)
  {
    SetItemPicture();
    GetItemPicture();
  }

  static void SetItemPicture()
  {
    ImportItemData service = new ImportItemData();
    service.UseDefaultCredentials = true;

    FileStream file = File.OpenRead("C:\\Temp\\bike.jpg");
    byte[] buffer = new byte[file.Length];
    file.Read(buffer, 0, buffer.Length);
    file.Close();
    string picture = Convert.ToBase64String(buffer);

    service.SetItemPicture("1000", picture);
  }

  static void GetItemPicture()
  {
    ImportItemData service = new ImportItemData();
    service.UseDefaultCredentials = true;

    string picture = string.Empty;
    service.GetItemPicture("1000", ref picture);

    if (picture == string.Empty)
      return;

    byte[] buffer = Convert.FromBase64String(picture);
    FileStream file = File.Create("C:\\Temp\\bike2.jpg");
    file.Write(buffer, 0, buffer.Length);
    file.Close();
  }
}

After running the SetItemPicture code, the next picture was imported in NAV:

image

17 thoughts on “Binary data with NAV Web Service

  1. Pingback: Binary data with NAV Web Service | Pardaan.com

  2. Hi,

    Nice example of how to it, it works very well but I noticed the data itself changed sometimes after export/import.
    In my example I used a datafile of 94960 Bytes and uploaded it via the webservice. After downloading it again I had 122880 bytes, all the
    extra bytes are blanks.
    It seems “MemoryStream.GetBuffer()” is responsible for this,
    when “MemoryStream.ToArray()” is used instead this extra Bytes are not added.

    <> Bytes := MemoryStream.ToArray();

    Kind regards,

    Bart Bourgeois

    • Hi Bart,

      You are right. Although GetArray() makes a copy in memory, while GetBuffer() doesn’t, that is not a big problem in this example. And GetBuffer() is known for adding some extra empty bytes to the end of a stream. It works with chunks of bytes instead of the exact length of the used bytes.

      So I agree that the GetArray() would be better here.

      Thanks for noting it!

      Arend-Jan

  3. Hi, I was wondering if you had a chance to get to experiment with this in Dynamics Nav 2013. I have this working in 2009 exactly as you have it and it works fantastic. When I try to run the same techniques in 2013 on the .Net GetItemPicture the ‘picture’ variable comes up blank after the service.getItemPicture(ref picture) and I can’t quite seem to figure out why. If you want you can email me, Thanks!

    • Thank you for this insight. But to Kevin, in NAV2013 NAV Streams are derived form System.IO.Stream and can be accessed directly.
      There is no need to make the call with a bigtext. Instead you can just use text without a limit (new in 2013) to communicate more easily.

  4. Hey,

    Is it possible to transport the blob file directly through the Webservice without the base64 encoding?

    Thanks 🙂

  5. No, that will not be possible. A blob is binary, and you need to package it in a SOAP XML message. So basically you need text in order to put in the XML message.

  6. Hi,

    is it possible to do it in navision 5 sp1? because the DotNet data type is not available in that version. is there any workaround for your code above without using the dotnet datatype? thanks in advance.

  7. Hi guys, I want a .zip file converting to Base64. I tried something like Find zip file from path, Converted to Stream and Stream to ByteArray[] but i could not Convert to Byte Array[] to Base64, I took an error message like “the function call was ambiguous. no matching method was found” How can i do it ? Or how can Convert a .zip file to Base64 in a different way.

    Thanks for your help.

  8. Hi there,

    I am ending up with a 160 kB bitmap, allthough I start with a 2 kB bitmap that I read into the .net-side of a similar solution. Both files, origin and result have the same image dimensions but the file size differs.

    Any clue?

    Best Regards
    Håkan Svensson
    Senior Developer
    CGI Sweden AB

  9. There seems to be a weird bug with MS on this one – the above code worked well when called from RTC, but when consuming from IIS/.Net it exhibited the following behavior:

    When setting the value of the BLOB field to something shorter than previous value the filed retains old length and values from the old field that exceed the length.

    For example, if the BLOB has value “1234567890” and you try to set it to “abc” then the result after calling is “abc4567890”.

    What fixed the problem was inserting CLEAR(Item.Picture); in SetItemPicture right after ITEM.GET()

Leave a Reply

Your email address will not be published.