File upload and download in AX 7

New Dynamics AX is a web application running in cloud, so how can users work with files in such an environment? The answer is: in the same way as with other web applications. If you know how to add and download attachments in your webmail client, you can do it in AX 7 as well.

And it’s not too difficult for developers either.

Let me demonstrate it on a simple form I’ve built.

FormLook

When you click the Upload button, a dialog opens where you can pick a file on your computer and upload it. It even shows progress of uploading.

Uploading

The whole upload is triggered by a single statement: File::GetFileFromUser(). You don’t have to deal with any details.

By default, the file is uploaded to a temporary blob storage and can be accessed through some ugly URL such as this:

Uploaded

If you click the download button, it will navigate to the URL and your browser will do the rest:

SaveFile

Code of Download button is again a one-liner: new Browser().navigate(fileUrl).

This is the complete code of the form, showing also how to get the URL of the uploaded file:

[Form]
public class UploadDownloadForm extends FormRun
{
    str fileUrl;
 
    [Control("Button")]
    class UploadButton
    {
        public void clicked()
        {
            FileUploadTemporaryStorageResult result = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
            if (result && result.getUploadStatus())
            {
                fileUrl = result.getDownloadUrl();
                info(fileUrl);
            }
        }
    }
 
    [Control("Button")]
    class DownloadButton
    {
        public void clicked()
        {
            new Browser().navigate(fileUrl);
        }
    }
}

Your files typically aren’t accessible by URL, because they’re in database or in a secured storage. But that’s not a problem. Just load the content of your file to a stream and pass it to File::SendFileToUser(). It will put the file to the temporary blob storage and navigate to the URL, therefore users can download the file in the same way as above.

60 Comments

  1. This is nice, but how do i create a file upload in a sysoperationframework in AX7?

    • Can you explain your scenario in more details? Nevertheless it sounds like a different topic that what I’ve discussed in this post.

      • You can just use the code in this example, with small tweaks, for a simple file upload.

        Create a contract with FileUploadTemporaryStorageResult as a data member, which SysOp will handle as it will look for nested contracts.

        Construct the AsciiStreamIO object in you processing class.

        If you leave the data member as visible you get to see how SysOp expands nested data contracts on to the dialog.

        • Hi Simon,
          what do you mean by “Create a contract with FileUploadTemporaryStorageResult as a data member”?
          Do I have to add FileUploadTemporaryStorageResult as a parm method to the contract? In this case it shows all parameters but no Upload Button.
          Could vou please show me a ode example?

  2. Hi Martin, great article!

    I have a situation where I need to download a file (image) from Azure Blob Storage (as a stream, byte array or physical file) and attach the image to a record (lets say a case for example). Any tips on creating the attachment manually through code?

    Thanks

    • You can use DocumentManagement.attachFile(), for instance. One of its arguments is a stream, where you’ll put your data.

  3. Hi Martin,

    Thanks for such a great article. In 2012, I have a batch job designed to export all the images, documents in a specific format from docuval table to a local folder. now that the file is no more a container, Is there a way where I can download all files(documents/images) from docuval to a folder directory?

    • If you mean a directory on some local machine (not in Azure), run an app from the local machine that connects to AX in Azure and get the data through a web service.

      • yes I mean a local machine’s directory. Thanks for the reply. I will check on how to connect to AX and get data via a web service.

      • would it be possible to drop files to a folder on another VM (in Azure) without using any app? May be establishing some kind of trust between these VMs/folders OR authenticating every time they communicate …

        Regards,

        • No, I can’t imagine how it should work. How do you want to set up a trust on a machine you have no access to? Don’t forget, production environments are deployed to Microsoft subscription and managed by Microsoft staff; you can’t connect there.
          Web services are the right way to communicate with cloud applications.

  4. Hi Martin,

    I need to import a excel file into AX 7, could you please suggest me some ideas ???

  5. Martin,

    i need to import a excel or csv using code into AX 7.
    My aim is reading a file from Azure blob using code.
    please suggest me some ideas.

    Thanks

  6. Hi Martin, great post! I used it a while ago to show an xml file I stored in the database in the browser, which worked, but now (maybe through the upgrade to 7.2) I get the following error in the browser when opening the file:

    AuthenticationFailed

    Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:319ab7fb-0001-0003-502f-341600000000 Time:2016-11-01T11:04:11.9893250Z

    sp is mandatory. Cannot be empty

    Any idea how to fix this? I tried to find an answer myself, but I came up empty.

    • Which line of code is throwing the error? If it’s a CLR exception, what is its type and stack trace?

      • It’s the “new Browser().navigate(fileUrl);” that gives the error. What I basically do is teh following. I have an xml string in my database, and I want to present that in a new browser tab.

        void show(str _xml)
        {
        System.IO.MemoryStream xmlStream;
        System.Byte[] byteArray;

        // convert string to stream
        byteArray = System.Text.Encoding::UTF8.GetBytes(_xml);
        xmlStream = new System.IO.MemoryStream(byteArray);
        FileUploadTemporaryStorageResult result = File::SendFileToTempStore_GetResult(xmlStream, “XMLView.xml”) as FileUploadTemporaryStorageResult;
        if (result && result.getUploadStatus())
        {
        url = result.getDownloadUrl();
        this.openUrl(url);
        }
        }

        public void openUrl(DocuPath _path)
        {
        Browser br = new Browser();

        if (_path == ”)
        return;

        if (DocumentFileHelper::checkUrl(_path))
        {
        // navigate
        br.navigate(_path,true);
        }
        else
        {
        // URL format is not supported
        checkFailed(“@SYS327425”);
        }
        }

        • Additional: I use Chrome, and the error I get is displayed in the browser, it’s not a stack trace error or something like that

          • Could you rather put your question, including details such as a picture, to a discussion forum? I contribute to community.dynamics.com and dynamicsuser.net.

        • Hi Jos,
          Did you manage to resolve the issue if yes please give me the code.
          I am also supposed to import file using X++ code;

  7. Hi Martin

    Thanks for the code, it is very useful

    But I’m facing a problem, I’ve create a text file, with a stream, I store the file with the function File::SendFiletoTempStore, the function returns the url, when use the line Browser().navigate(url), my browser open in a new tab the file, there is another way to Download as a file.

    WebClient.DowloadFile no works too,

    Thanks and Regards

    • Jose, it’s not clear to me what’s the problem. What you describe sounds correct, so it’s likely about what you *expected* instead. WebClient is a very different topic, because this blog post is about the server side, not client side.
      I suggest you elaborate your question in a forum, where you’ll also get a few thousand of potential advisors.

    • Hi Jos,
      Did you manage to resolve the issue if yes please give me the code. I have a XML string stored in container in Ax table. Trying to show XML to user from a form and open as URL in browser.

  8. Hi Jos,

    Did you find any solution to the issue, facing the same issue.

  9. Hi Martin,
    I need to save the attachment (say a report ) generated at runtime to a invoice journal record. I am trying to use the same code as print archive functionality in ax7. The records are stored in docuref and docuvalue tables but when I click on attachment icon , attachments doesn’t show up.
    I verified adding attachment of type notes. I am able to see notes in attachment form but not the documents attached in PDF format.
    I also noticed that the ‘location’ field is empty in the table record.
    please let me know your suggestions

    • It depends on what you mean by that; please elaborate your question.
      By the way, you’ll reach many more people if you ask in a discussion forum (I contribute to dynamicsuser.net and community.dynamics.com).

  10. Hi Martin,
    Scenario = A batchjob ( SysOperation framework ) runs at night and polls a local folder for flat files that come from my payroll provider. AX processes these files and stores resulting files in another local folder where they are picked up by separate HR-application. Is this scenario still possible with Dynamics 365 ?

    • Your goal is achievable, but how depends on many variables. For example, if “local folder” means local to the AOS server or to you (so you would need an Azure application actively connecting to your computer). Also, it depends on if you insist making the connection from AX, which is the opposite way how it’s usually done. Or if you insist on using a local folder instead of a network one.
      I suggest you create a thread in a discussion forum (I contribute to community.dynamics.com and dynamicsuser.net); it’s a more suitable place for long discussions.

  11. Hi Martin,
    thanks for the post. The Command “File::GetFileFromUser()” uses the “FileUploadTemporaryStorageStrategy” as Default Strategy. The “FileUploadTemporaryStorageStrategy” gets the Destination for the File from SharedServiceUnitStorage::GetDefaultStorageContext(). In my Case the ConnectionString of the Default storage context is “UseDevelopmentStorage=true”. I now want to stop using the Emulator and start using the Blob Storage in the Azure Cloud. My Question: Do you know how I can alter the Default Connection String or what it depends on?

    (SharedServiceUnitStorage is part of the Microsoft.DynamicsOnline.Infrastructure.Components.SharedServiceUnitStorage Namespace, which is a member of the Microsoft.Dynamics.Clx.ServicesWrapper Assembly, which is a member of the Microsoft.Dynamics.Clx.ServicesWrapper.dll, stored at C:\AOSService\PackagesLocalDirectory\bin\Microsoft.Dynamics.Clx.ServicesWrapper.dll (In Case it helps))

    • So I just fixed it:
      “SharedServiceUnitStorage::GetDefaultStorageContext()” gets it´s Value from the web.config file in the Directory ‘C:\AOSService\webroot’. The StorageConnectionString “AQAAAHNm4l3JT6ikAPoAN//zuzANlILUc2biXclPqKQA+gA3//O7MA2UgtR3NT3UDbgMyD1HIznpaNDGEFF1PsxJvUQm15MUIpNU4ypDHRSbzymhBnCXnfGWB9EyazceEBSrGtaW2Ol/iy+JdI0u0iqDpdVDZiPvX17eG+NW3lpuhazhV8PP/ChOBg5YUs3Nre4WnyHZok2UxnAJfazwf0tiz17lDzV6acH+3VrmTDEhnjxg4z3kicquPm7qUA/bFO1Hr3ejZoubPYsVSulUiAwAN8xlQKYIJN3f9mzhZKyA+6rUWp9LP/c1FLQ5EFUiJE7Md3ft8qWjxGBCHYSYb7qvQUN9QuQVRdxg69lQ2dgXDDS/ylNqlCJYX4+PMq6K8yIq12qeH4X5cEvPHh2q1wNpqzvomS7m5P7KbdkWBKgBmoD+wYAimt8EIPGq0O7E2XhoOTg/fL3RaVxvlP4lOfa5GJUnTf34HRFocby9Yy3lJiYwpKWr94yHp6zmVx9vhLLGOlsb2GiKwAYyA6j9xLEUyW8j1I/WJ58OHs53G+93Q3COyaQao1yfYQu8X8JiwXN62GinsKxA3ysPED3HnaIINW1mb2Z19+30n6b8WDI3kQioMRhMJuom/IpHN1Nb0lbWK1yrmB5HbLlMClYesdXrqI2ogD0JTAGzEaXTsTMG/BBupE5kqcQJyZps5qkumR7C8bt95ExuhYUMrDXwcT6rDFO/fr915pUM9jTjryKwDrI0uhykTyRuqnAzfsvkO3Tjkwr09ntwgZbr0IH/DH3fF68EWJzqXE5cZw==” seems to reference “UseDevelopmentStorage=true”. When you change this ConnectionString to your custom ConnectionString “SharedServiceUnitStorage::GetDefaultStorageContext()” also uses your custom ConnectionString.

      Hope it helps someone who had the same Problems 🙂

  12. Hi, I need to store local file path into a table, in AX 2009 i used filepath EDT , but in AX 7 it is deprecated and it involves in blob storage,i could get only the URL. Is there any possibility to get the local file path in AX 7?

    • Of course it’s possible to upload local files to AX – it’s exactly what the blog post above shows.
      Whether you store them in Azure SQL (as you want) or in another Azure storage (blob) is just an implementation detail. But a separate storage is recommend to avoid bloating up the database.
      If you meant storing something on a VM with AOS, forget it. It wouldn’t work with AOS clusters (which is true in AX 2009 too), because the local path wouldn’t be valid on other application servers.

      • Hi Martin,

        I am using CommaTextIO for importing CSV file, and it accepts 3 parameters CommaTextIO(str fileName, Str Mode, [int codepage]), I want ‘fileName’ parameter to be passed, I am passing the URL(result.getDownloadUrl()) as fileName string, which is returning Null in CommaTextIO. As it accepts only local path(Not blob). how can i get the ‘fileName’ parameter in this case ?

        I am using CommaTextIO to overcome the comma values in field text. Could you please help me in this ?

        • You can get a stream from result.openResult() and read it with CommaTextStreamIO.
          If you insist on using the file-based API, you’ll have to download the blob to a file (using getDownloadUrl()).

  13. Hi, I am converting a .xlsx file into .csv file.While converting the file, the content type was set to “filecontenttype=null”, which makes impossible to read my .csv file using AsciiStreamIo::constructForRead(stream) ,but the filecontenttype should be “application/vnd.ms-excel”. I need to set my content type before i am converting, the file. Here is my code, give me any idea.

    using (ExcelPackage Package = new ExcelPackage(memoryStream))
    {
    var Worksheets = Package.get_Workbook().get_Worksheets().get_Item(1);
    filename = splitFilename+’.csv’;
    memoryStream = Package.Stream;
    FileUploadTemporaryStorageResult fileUploadResult1=file::SendFileToUser(memoryStream,filename) as FileUploadTemporaryStorageResult;
    memoryStream=fileUploadResult1.openResult();
    asciiIO = AsciiStreamIo::constructForRead(memoryStream);
    ………
    }

  14. Hello Martin,
    Great post!! I have used it in my case where i need to save MergedPdf file to local machine using below code
    FileUploadTemporaryStorageStrategy fileUploadStrategy = new FileUploadTemporaryStorageStrategy();
    FileUploadTemporaryStorageResult result = fileUploadStrategy.uploadFile(mergedFileStream, blobReference, “”, “.pdf”) as FileUploadTemporaryStorageResult;
    if (result && result.getUploadStatus())
    {
    fileUrl = result.getDownloadUrl();
    info(fileUrl);

    new Browser().navigate(fileUrl);
    }
    Here, i need to save all the PDFs in temporary storage and at the end i need to give a single popup using that i can save all to my local machine instead of giving popup for every single PDF, Could you please suggest me how this can be achievable. Thanks in advance 🙂

    • Create a single zip archive with all the files and let users to download this zip file.

      • Thanks for response Martin, i am on my trials but could you also please provide some code snippet/reference links for the same, corresponding to above scenario. thanks

  15. “By default, the file is uploaded to a temporary blob storage” – can you give me a hint of how I can upload file not into temporary blob storage, but into SharePoint folder (presuming that all sharepoint settings were accurately populated and I have an information about the final sharepoint folder).

    • The first question is whether you should do it at all. Microsoft doesn’t. If you look how they handle document attachments, they first upload files to the temporary storage (this step is the same regardless of whether you store files in DB, SharePoint or so) and only then they let a particular storage provider to take the file from the temporary storage and put it somewhere else.
      To get the SharePoint provider, you can call Docu::GetStorageProvider() with _docType.FilePlace equal to DocuFilePlace::SharePoint.

  16. Hi Martin,

    I have a requirement where I have to run a file generation process in batch. I used
    FileUploadTemporaryStorageResult class and stored the downloadUrl(which stores the temporary file path) string in a table when the batch executes. But this url expires after 2 hours since it’s a temporary storage. Can I leverage any existing classes to upload file permanent in azure?

    • Look at Docu::GetStorageProvider(), it might help you (depending on what exactly you need, which isn’t completely clear).

  17. Hello Martin.

    Please, can I use this class to upload multiple files? I need to create a XML file read, but the user would like to select more than one file. Is it possible?

    Thank you.

    • I don’t think you can select and upload several files at once. But users could upload an archive that you would unpack on server.

  18. Hi Martin,

    Hope you are doing well!

    Requirement below –
    User will saved file on Azure blob and Provide Azure Url to me as comsume service.
    Once I will get Url by service, I need to download the excel file from URL and doing operations on file and upload on Azure .
    How we can download a file from Azure URL?
    if file will downloaded, where I can get a file?

    Thanks,
    Rohan Sodani

    • You can use CloudBlob.DownloadToFile() to download a file from Azure blob. Note that there are other related method, e.g. you can download the content as a stream.
      Nevertheless this is a very different topic than what I’ve discussed in my blog post, therefore this isn’t the best place for such a discussion. If you need more details, you should rather ask in a discussion forum. I’m active in community.dynamics.com and dynamicsuser.net.

      • Thanks for reply.

        I am not getting, can you please provide me sample code?
        can we use browser.navigate(url) ?

        thanks,

  19. Hi Martin,

    I am using code new browser.navigate(url) and I am getting popup, how I can hide that popup?

    Thanks,
    Rohan Sodani

    • I thought that “download the excel file from URL and doing operations on file” means that you want to use the file in AX. But now you’re trying to use Browser.navigate(), which does a completely different thing – it navigates user’s browser to the file. Either I have no idea what you’re trying to achieve or you’re playing with code that has nothing to do with your requirement.
      Please create a discussion thread at https://community.dynamics.com/365/financeandoperations/f/765 and explain in detail what you actually need.

  20. Hi Martin,

    I am exporting data from AX to csv, I have a field which contains commas(,), double quotes (” “) and line breaks (\n) as field value, When the field value contains comma(,), it considers as a different field and going the data into a different field, to overcome that issue, i put double quotes around the field value and comma issue is resolved, but now as i said earlier, we can also have double quotes as data in field value, then the data of that field is splitting into multiple fields and multiple lines due to commas and line breaks as field value.

    Eg: string field holds the below data.which contains (,) and double quotes(“”) and line breaks (\n)

    “Wishing you a happy new Year”

    Regards,
    XXXYYYZZZ.

    How can i export the data will all spacing and formatting into CSV file and how can i re-import the same back again into AX ?

  21. Martin,

    Is it possible to add drag and drop to this file upload control?

    Bjorn

    • You can create custom controls, but I don’t think you can extend the existing control in this way.

      • Thanks!
        I noticed that MS is using jquery fileupload (https://github.com/blueimp/jQuery-File-Upload) for their upload control. This library includes drag and drop, but it looks like they did not implement it fully. There is an attribute on the control called Drag Drop, which can be set to manual, and it says code is needed, but I haven’t not been able to find any examples of this online. If I were to create a custom control with this same jquery library, how can I know which url to send the file to?

  22. In AXR3 I upload excel files into the general journal. It appears from your original post I could use this instead. In your code, can you give an example of where I would actually update a field in a table.

    • Don’t reinvent the wheel – the feature is already there. Open Data management workspace, create an import project, press Add file, select the target entity and the source data format (Excel) and upload the file.
      Here is documentation: Data management.

  23. I have a FormA that upload the file then on FormB where call a class to process some logic to get the uploaded file for processing. Is there a way I can implement this?

Comments are closed.