Published On: April 13, 2022Categories: Innovation, Technical

Knowledge sharing is one of the basis and an everyday practice between our team members. So, we decided to share another fix to a challenge one of our colleagues encountered in his everyday tasks as a requirement for the client. Hopefully, this blog post will help you as well.  

Recently we got a client requirement to replace FTP upload for specific files with SFTP. After our team member has done some research, he found this post which was followed mostly. However, he wants to add a few things that he had to figure out during development. First, you’ll need to create a DLL which you will use to connect and upload the file to SFTP. Prior to that, you need to install two DLLs for referencing through the NuGet packages for the solution:

sftp blog post 1

If you don’t have the NuGet packages initially you can download them from:

Go to settings of NuGet manager and add the new folder with the two new packages:

sftp blog post 2

sftp blog post 3

It is very important first to install SSH.NET and then Renci.SshNet.Async. After installation without errors you should see them in the list of installed packages:

sftp blog post 4

After you are done with the prerequisites you can create a new project of type C# class library in the same solution as the D365 project:

sftp blog post 5

Add the following code for the C# class:

 

 

using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using Renci.SshNet;
using System.Text;

namespace SFTPFileTransfer
{
    public class SFTPConnect
    {
        public static string uploadSFTPFile(string host,
        string username,
        string password,
        System.IO.Stream sourceFile,
        string destinationPath,
        int port,
        string fileName,
        string privateKeyFilePath = "")
        {
            string successStr = "Fail";
            List<AuthenticationMethod> methods;

            /*It depends if the private key file is present for authentication. If the SFTP is key secured then the private key file has to be passed.*/
            if (privateKeyFilePath != "")
            {
                            var privateKeyFile = new PrivateKeyFile(privateKeyFilePath);
                            methods = new List<AuthenticationMethod>
                {
                    new PasswordAuthenticationMethod(username, password),
                    new PrivateKeyAuthenticationMethod(username, privateKeyFile)
                };
            }
            else
            {
                methods = new List<AuthenticationMethod>
                {
                    new PasswordAuthenticationMethod(username, password)
                };
            }
            try
            {
                var connectionInfo = new ConnectionInfo(host, port, username, methods.ToArray());
                using (SftpClient sftpclient = new SftpClient(connectionInfo))
                {
                    sftpclient.Connect();
                    sftpclient.ChangeDirectory(destinationPath.Trim());
                    sourceFile.Position = 0;
                    sftpclient.BufferSize = 8 * 1024;
                    sftpclient.UploadFile(sourceFile, fileName);
                }
                successStr = "Pass";
            }
            catch (WebException e)
            {
                successStr = "Fail";
            }
            return successStr;
        }

    }

}  

 

Rebuild the solution and you will now have the DLL file in the project directory\bin\Debug folder. Copy it to AosService\PackagesLocalDirectory\<your model>\bin folder.

Now to the X++ code, you should have parameters for all the needed fields:

Username: username of SFTP

Password: password for SFTP (container field to be filled using edit method or any encryption method), for simplification in this demo I’ll use plain text

HostName: this can be an ip address or <domain>.orgs.in

Port no: 22 by default for SFTP

Destination folder: /upload by default while an SFTP is created. This can vary but the input should start with “/” which depicts the folder in SFTP where the file will be dropped

//Upload to SFTP
        System.IO.StreamReader  reader;
        System.IO.Stream        requestStream;
        System.Byte[]           bytes;
        System.Text.Encoding    utf8;
        System.IO.Stream        stream = io.getStream();
        stream.Position = 0;

        UserId sftpUserName = companyInfo.SFTPUserName;
        str sftpHostURL = companyInfo.EDISFTPHost;
        int sftpPortNum = companyInfo.EDISFTPPortNum? companyInfo. SFTPPortNum: 22; //22 is default port for SFTP
        FilePath sftpDestinationFilePath = companyInfo.FileDestinationPath;

        if(sftpDestinationFilePath != "")
        {
            reader = new  System.IO.StreamReader(stream);
            utf8 = System.Text.Encoding::get_UTF8();
            bytes = utf8.GetBytes( reader.ReadToEnd() );
        
            reader.Close();
            try
            {
                
                Password sftpPassword = companyInfo.SFTPPW;
                str success = SFTPFileTransfer.SFTPConnect::uploadSFTPFile(sftpHostURL, sftpUserName, sftpPassword, stream, sftpDestinationFilePath, sftpPortNum, fileName);
                if(success == 'Pass')
                {
                    Info(strFmt("%1 File successfuly sent to SFTP ", fileName));
                }
                else
                {
                    Error("Error in sending file to SFTP due to incorrect Host/Port/User Credential");
                }
            }
            catch
            {
                Info(infolog.text());
            }
        }
        else
        {
            reader = new  System.IO.StreamReader(stream);
            
            File::SendStringAsFileToUser(reader.ReadToEnd(),fileName);
        }
        CodeAccessPermission::revertAssert();

 

That is all the development, you can test the solution if you have an SFTP host to send files to.

Let us know if you have any questions, and don’t forget if you are interested in a potential career opportunity in our company, check out the careers page for our open vacancies.