Drag and Drop File Upload for Microsoft Dynamics NAV 2013 R2

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:https://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!

Vjeko

Vjeko has been writing code for living since 1995, and he has shared his knowledge and experience in presentations, articles, blogs, and elsewhere since 2002. Hopelessly curious, passionate about technology, avid language learner no matter human or computer.

This Post Has 64 Comments

  1. Filip

    Vjeko, great post!!

    Defenetly gonna try this one :))

  2. Peter Tijsma

    Thanks Vjeko!

    Again, great presentation yesterday and i’ll make good use of your examples!

  3. 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.

  4. Pieter Korf

    Go Vjeko Go!! Thanks for the examples and the clear instruction!

  5. Arndt

    TAP TAP TAP TAP 🙂

  6. Aize

    Thanks for another great session, Vjeko!

  7. Luc Van Dyck

    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. Peter Tijsma

      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. Vjeko

        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.

  8. Luc van Vugt

    Dear friend, thanx for having been with us and sharing your expertise. Cu soon. B rg Luc

  9. Harry

    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. Vjeko

      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.

  10. bert

    Is it possible for u to provide this solution also for Nav 2013/Visual Studio 2010 ?

    1. Vjeko

      Yes, it is possible. I’ll do that at my earliest.

      1. Daniele Rebussi

        Hi Vjeko, this seems a very good solution!
        We’d like to use it, was a version for NAV2013 published in the end?

        Thanks
        Daniele

        1. Vjeko

          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?

          1. Daniele Rebussi

            Thanks for fast feedback, looking forward to it!
            I will keep monitoring the blog 🙂

          2. Peter Tijsma

            Now I can’t wait to see your solution and how you solved the >40Kb issue 🙂

          3. Tillian

            Hi, are you still planning on upgrading the extensions?

          4. Tillian

            Hi, are still planning to update the extension?

          5. Vjeko

            Yes, I am planning on doing that, just don’t ask me when exactly – time is not really on my side…

  11. Povl

    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

  12. Povl

    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

    1. Rejh

      Hi Povl, is it possible if you could share your script? thanks.

    2. Indirect

      Hello Povl!
      Could you please share your modified solution, if possible?
      Many thanks!

    3. mark higgins

      Pov

      Could you share your code?

      thanks

      1. Vjeko

        The code is attached to this post. Can you not find it?

        1. mark higgins

          I was looking for Povl’s modified version, cannot seem to find it?

  13. Peter

    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?

  14. Peter

    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.

  15. Carsten Scholling

    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

  16. Eamonn

    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!

  17. ptijsma

    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. Krisje

      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 🙂

  18. AB

    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

    1. Vjeko

      Could be IE version, I am not quite sure. I haven’t tested it on IE8. Can you try it with a higher version?

      1. AB

        Thanks Vjeko, will try to upgrade IE version.

  19. Duilio Tacconi

    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.

  20. Rob

    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

  21. Andras

    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.

  22. Oscar

    Hi,

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

    Thanks
    Oscar

      1. Oscar

        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

  23. Joao Leite

    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

    1. Vjeko

      Thanks! I am pretty sure it would work. Some googling + stackoverflowing would surely tell you how.

    2. Vjeko

      It sure would be possible.

  24. JL

    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. Davide

      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

  25. Joseph Rathburn

    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.

  26. Robert P

    Hi Vjeko,
    any idea of how to implement this to Business Central?

    1. Vjeko

      Hi Robert,
      Yes, this can be done in Business Central too, mostly the same way it has been done in NAV. I don’t remember what I did in the C/AL part, but if I used any of .NET classes, they are not needed any more because whatever they do can certainly be replaced either with AL or done in JavaScript. I am thinking of redoing this control add-in for Business Central.

      1. Pere Alumbrero

        Remake BC365 Maybe with https://www.dropzonejs.com/? 🙂 I’m trying it right now, but I’m not so good programmer and there are not many examples of BC365 ControlAddIns yet on the www.

        1. Vjeko

          Aaah, the good old evergreen Drag and Drop. You wouldn’t believe how many times I’ve set out to do an update on this one, but I never did. No promises here…

  27. Rajesh Gan

    Hi Vjeko,

    Nice Post , Liked file, but is that only takes image drag and drop, how about the file drag and drop,
    Need some help on files drag and drop.

    Thanks,
    Rajesh

    1. Vjeko

      Hi Rajesh,
      This is something that people often ask, but there is no difference in dragging and dropping images or any kind of files. Images are binary files, and this shows you that you can handle any kind of file out of the box. If what you are trying to do is show a preview of any kind of file (PDF, Word, Excel, PowerPoint) then this is a completely different topic, and it will require you to use a library which can do the preview for you.

  28. Clinton

    Hey, I know this was done a while ago… not sure if I’ll see a reply…. but I was wondering if it would be easy to adapt this to the new ControlAddin object brought into use in the new AL development environment for recent versions of NAV/Business Central?

    1. Vjeko

      Yes, of course it can be done, however I never make any promises here on my blog. If I get to do it, I’ll do it, if I don’t, then I don’t. Keep in my that this blog is my personal time and personal effort and is for free, and what you see is what you get.

  29. nagarajan

    Its very good. we need to user for this.

  30. nagarajan

    Hi Vjeko,

    I have done the steps same mentioned in the Remade file from Drag Drop folder. After I don’t know how to test. I tried to drag the documents to NAV 2018 window, but no response.
    I am using NAV 2018 trial version. I want drag and drop documents/files to NAV and vice versa.

    1. Vjeko

      I am not sure what’s going wrong, where, or why – I have difficulties visualizing what you are doing exactly. Does the control add-in show in the page? If yes – what happens when you drag a document onto the control add-in?

  31. Sebastian

    Hi Vjeko.

    I actually implemented my own dropzone functionality in JS (which work perfectly in a web client) before I saw your solution. I began searching for other solutions when I tried my dropzone in a RTC client and unfortunately found that I could not drop files. I am somehow simply not allowed to drop any files in the RTC client. I get the “forbidden” cursor as soon as the file that is being dragged hits some of the RTC client windows.

    Do you have any idea of what is happening? I also downloaded your solution and tried that one, but with the same result unfortunately. In a C# control add in solution there should be a Control.AllowDrop property which should enable file drops but I can’t find anything equivalent for a JS solution. Is there something I have misunderstood?

    Best regards
    Sebastian

Leave a Reply