Drag and Drop File Upload for Microsoft Dynamics NAV 2013 R2

Yesterday evening I spoke at Dutch Dynamics Community event, on invitation by my dear friend Luc van Vugt, and the topic was control add-ins for NAV 2013 R2. Of course, this automatically meant that the audience should see more JavaScript code than C# or C/AL, and that it should be something both fancy and useful.

So how about this: you drag and drop a file onto an NAV page, and the file is automatically uploaded and stored in a BLOB field in the NAV database? And yes, it does the same no matter if you call it from the Windows or the Web client. And yes of course, no external components or dependencies needed.

As I promised, I would make all the source components available for download after the sessions, and if you just want to take the components, here they are, ready to download, install and abuse:http://vjeko.com/wp-content/uploads/2014/03/DragDrop.zip

If you want to know how this thing works and why, read on. Otherwise, just download the thingy, install it (the instructions are included with the file) and abuse it to your fancy.

Okay, I see you decided to stay. Good.

I’m sure I don’t need to explain the part about how to create an assembly including the interface that explains to the C/AL compiler which events and methods are supported on the new component. This makes my job easier here, so I’ll focus on the JavaScript right away.

image

This piece of code appends some HTML to the control:

  • DIV with ID #drop-files to act as a drag & drop host control. It is styled using the embedded CSS
  • DIV with ID #drop-text, which is displayed only if the image is not present
  • IMG with ID #drop-img, which shows the image if it is present
  • DIV with ID #drop-clear, which acts like a button and invokes the C/AL trigger to remove the image from the BLOB

image

This function receives the image represented as data URI. If there is data, the image is bound as the source of the #drop-img element. If there is no data, image is hidden, and text is shown.

image

This is the workhorse, from the JavaScript end. This piece of code first enabled the data transfer for the document, and binds a function to respond to the drop event of the DIV control created earlier. The event receives the collection of files (yes, it is technically read to receive multiple files) and then for each file in the collection it initiates the download through the readAsDataURL method. Also, the FileReader instance responds to the onload event by receiving the image data represented as data URI, checks whether the data is indeed image, and does two things – it calls the SendData function I explained earlier and then calls the NAV DataRead trigger to pass the received Data URI image.

The remaining JavaScript code is there to enable drag and drop effects, clicking the DIV to send the request to clear the image, and to instantiate the control. Let’s move to C/AL.

image

This function is called from the DataRead event trigger, and it receives the text representing the image as data URI. It first checks whether the data URI is correct through a regular expression. If it is, then it takes the part of the match that represents pure image data in Base64, converts it to binary, loads it into a stream, and then copies that stream into the BLOB. Here, the image data is stored into NAV.

One thing remains: to send image data to JavaScript if it exists.

image

This function loads the image data into the stream, then loads the stream into an Image object to check its format, which is necessary for the data URI that it must return. It starts constructing the data URI by specifying which image format it is, and then finalizes it by converting the memory stream into the Base64 encoded string.

image

Finally, this method calls the control add-in, and invokes its SendData function. This in turn calls the same SendData function in JavaScript that I’ve explained earlier.

And that’s all. Simple, and functional.

Now, if you haven’t downloaded the demo, don’t waste any more time, just grab your copy and put it to work. If you find it useful, please come back to tap my shoulder and to share with the world how happy you and your customer are with this component and with how much time it saved in your implementation. Cheers!

49 thoughts on “Drag and Drop File Upload for Microsoft Dynamics NAV 2013 R2”

  1. Thanks fot the intresting session and sharing this expertise. Great stuff these Javascript Control Add-Ins. I was looking for a advanced “Hello World” example for a while and found it in this solution. Time to start upgrading my obsolete HTML and JavaScript skills now.

  2. Thx for this great example code. If I want the dragged file to be saved on the server in a shared folder, I presume the code to copy this file should be placed in the JavaScript part?

    1. Hi Luc,

      I wouldn’t do this via Javascript since the client typically will not have access to the server file storage.
      The solution is quite simple though by modifying the C/AL code;

      Instead of this code in the SavePicture function:

      Rec.Picture.CREATEOUTSTREAM(OutStr);
      COPYSTREAM(OutStr,MemStream);
      Rec.MODIFY;

      It can easily be changed to something like this:

      Image := Image.FromStream(MemStream);
      Image.Save(,ImageFormat.Png); // If you want to save it as .png file

      With these variables declared:
      ———————————————————–
      – Image = DotNet “‘System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.System.Drawing.Image”
      – ImageFormat = DotNet “‘System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.System.Drawing.Imaging.ImageFormat”

      1. Actually, once you have it in the stream, no need to load it into an image. Just create a file, then call the CREATEOUTSTREAM on the file. Also, change the regex to catch aby binary fike, not just images.

  3. Vjeko, great post!

    But still have an issue with large images.
    It works fine with small images (<48K)
    When dropping larger images (in the webclient) , the following error occured: "Communication with the server failed, and the content cannot be displayed. Refresh the page or open a new browser window."

    Monitoring with firefox + firebug results in the following http error 413: request entity to large.

    Any ideas how to solve this?

    Thanx,

    Harry

    1. Great catch! About the solution ideas, I have two, one of which I am not quite sure about. One is to try to play with the amount of data the NST can receive. The other is to send data to NAV in smaller chunks. If I get time, I’ll provide both, but cannot promise when it will be.

        1. Hi Daniele, thanks for the comment! I am currently working on a new version which will work on all (2013, 2013R2, 2015) versions, will support multiple-file drop, and will fix the issue with >40KB upload through web client. I’ll publish it on my blog very likely this week, next at latest. Is that okay?

  4. Hi thanks for the the solution ! Works really cool.
    I just wish I had the skills to modify so I can also drag / drop e-mails 🙂

    Cheers from Denmark,
    Povl

  5. Thanks again. I learned a bit of JavaScript, and modified your script so it can be used for any datatype and also get the filename, type, size and blob to a new document table !
    Some of the best things in life are free!
    Dragging Outlook mails have proved too big a challenge for now

  6. Hi,

    we also have the file size problem, so we can’t upload files >48 KB.

    I tried to send the BASE64 String step by step in “smaller” packages and merge them in NAV.
    But this also occurrs the 413 Too Large error.

    Have anyone an idea?

  7. The NAV / Web Client configuration file we also modified. We changed the chunk size, but this has no effect.

    Also, we tried some configuration modifications on IIS but we get everytime the 413 error if we upload files >48 KB.

  8. Hi,

    The issue with the error message 413 is, that the number of Bytes exceeds an internal buffer. You may split the data into smaller chunks (and also use File.Slice() API to support very large data files).
    However, all communication between the browser and the IIS via the Microsoft.Dynamics.NAV.InvokeExtensibilityMethod() is asynchronous, meaning that the JavaScript call to InvokeExtensibilityMethod returns immediately – long before the actual trigger gets executed on the Dynamics NAV Server.
    The Dynamics NAV Control Add-in API does not expose information about when an InvokeExtensibilityMethod() call is complete and the Dynamics NAV Server is done executing the Trigger. This is needed to synchronize the code in the control add-in.

    You may play around to only send small chunks and signal the web browser after data has been processed. This may also need to include code to decouple method calls and avoid a ping-pong call stack between C/AL and JavaScript using setTimeout().

    Cheers
    Carsten

  9. Hi Vjeko, thanks for all the very useful demonstrations and How-To videos online!
    I’m hoping you might be able to shed some light on a problem I’m having trying to display a c# winform control add-in in the web client. I have a calendar control add-in created in C# that runs ok in windows client but not in web client, this was understandable as there was no manifest file and interface C# program containing triggers/methods.

    So I created a manifest file that invokes a C# interface program method which then embeds the original C# calendar program packaged as an activeX into the web client as a HTML object(similar to your how-to video on WMP). This method works ok for a simple HelloWorld() method but not for a winform createcontrol() method. Do you know is it possible to achieve what I’m trying to do i.e. embed a c# winform into the web client?

    Thanks!

  10. Based on Carsten’s suggestion, i managed to get a file-upload working with files much larger than the 48 Kb limit.
    It basically means doing these steps:

    1. Call an event “OnFileUploadInitiate” from the Add-in
    2. This event in NAV on it’s turn saves the number of files to receive in a global variable and fires a function in the Add-in to start uploading a single fileblock (UploadSingleFileBlock(1,1))
    3. The Add-in then starts the upload for the first fileblock with a size as specified in advance (windows clients are allowed to send much bigger chunks then the Web Client). It’s using a different event that i called “UploadFileBlock” which sends this file block to NAV with a few additional parameters (CurrentFileCount, BlockSequence, BlockData, TotalBlocks)
    4. Based on the information received NAV decides if it needs to fire the same function in the Add-in again by incrementing either the fileblock or filecount (i.e. UploadSingleFileBlock(1,2))
    5. It keeps repeating this process until all blocks for all files are received.

    Key thing here is that the Control Add-in should Always be in control of sending the blockdata.
    At first I tried to try a kind of ‘pull’ scenario so NAV pulls the data from the Add-in, but this Always failed. I don’t know the reason for this, but i’m guessing it has to do with the fact that NAV is in fact unaware of the Web Client (since there’s no active connection) and therefore fires multiple events at once to the control add-in in parallel.

    When the Control Add-in is in charge of firing the events and sending the data, NAV only has to listen and comply to the request.

    Hope this helps for anyone still fighting the issue.

    1. Hi ptijsma,

      I have the same issue on my Addin, on a normal NAV Client all works fine, when using the same addin in Webclient I get the communication error … Could I get your solution to help me out ?

      PS, Vjeko, again BIG kudos for your blog, with the example I had 99% of my scope covered 🙂

  11. Hi Vjeko, thanks for this post. I tried to use your control and I get this error message “An error has occured in the script on this page”…. do you want to continue running scripts on this page. Any suggestions?

    User has full permission on client and server. I’m using version IE8. Could it be IE version?

    Regards

    Amit

  12. HTTP 413 has been resolved with CU 10 for Microsoft Dynamics NAV 2015.
    NAV Development Team bounded the WCF MaxReceivedMessageSize to system.web/httpRuntime maxRequestLength.

    A zillion thanks to Vjeko to drive the NAV development world into the sky. And higher.

  13. Hi all, I’m finding the possibility of drag and drop very interesting and I got Vjeko’s solution to work, thanks for that.

    But I’m quite interested in povl’s adjustment to be able to drag and drop any file.
    Is that adjustment posted somewhere as well?

    Thanks,

    Rob

  14. Hi,
    How much change this code to Drag and Drop any type of file not only Images? I would like to store to a Record a Drag and Drop file.
    Anyone can have article for it?
    Thank you.

  15. Hi,

    Very nice article! If saving to disk, is there any way to extract the original filename?

    Thanks
    Oscar

      1. Thanks but I don’t understand what to put in getElementById(”)

        I have tried all id:s I can think of. Jquery seems a little different than the standard file input

  16. Congratulations Vjeko, for this excellent article!

    What if you just to drop a web link to a file (e.g. from O365 Sharepoint) and record the link in NAV, instead of the full physical file? Would it be possible with a tweak of your demo code?

    Many thanks

  17. Hi Vjeko! tried to create use page 59901 in my NAV 2017 “Items List” page as a factbox, and got in the RTC “The metadata object 59901 was not found.”. Then tried to compile the 59901 Item Picture Factbox object and got an error for the “CREATEOUTSTREAM” unknown variable… And with my restricted NAV license, can’t even open it in design mode… Any ideas?

    Error No. Object Type Object ID Object Name Error Type Function/Trigger Line No. Description
    1 Page 59901 Item Picture FactBox Error SavePicture 5 You have specified an unknown variable. CREATEOUTSTREAM Define the variable under ‘Global C/AL symbols’.

    1. JL: Maybe it is because NAV2017 changes “Picture field” type. Try to add a new BLOB field in Item table and CREATE OUTSTREAM will work again. I just tried it on NAV2017

  18. Hello Vjeko:

    thanks for your wonderful function….I have loaded into NAV2016 but does not seem to work. We would be interested in contracting with you to load and adjust this function a little for our NAV2016 application.

Leave a Reply