Dynamics 365 – Azure Blob Storage Dosya Yazmak(CSV)

28 Oca

Herkese merhaba,
Bu yazımda sizlere Dynamics 365‘te x++ ile Azure Blob Storage dosya(csv, txt dosyaları oluşturmak) nasıl oluşturulduğunu anlatmaya çalışacağım.

Herkesin bildiği üzere Dynamics Ax 2012 ve öncesi için tüm sistemlerde paylaşılmış klasör yapıları mevcut; “\\fileserver\ortak\” gibi. Dolayısıyla classlarımızda bu ortak klasör yapılarını kullanıyor ve herkesin(ofis içi) erişebildiği dizinlere dosyalarımızı yazıyor ya da okuyorduk. Dynamics 365 ise Azure ortamında koştuğu için bizlerde Azure‘da bir yere bu dosyaları oluşturmalıyız. İşte tam bu noktada Azure Blob Storage devreye giriyor. Tüm dosyaları burada kolayca yönetebiliyoruz.
Ben de bununla ilgili olarak kullanabileceğim bir Class yaptım. Bu Class ile kolayca Azure Blob Storage sistemine bağlanıp dosya yazabiliyor ve indirme linkine ulaşabiliyorum. Umarım sizlerin de işine yarar.

İlgili class;

// Azure blob storage dosya yazar. Download linkini verir.
// Semih Çelikol - semihcelikol.com
class SmhAzureBlobStorageHelper
{
    Microsoft.WindowsAzure.Storage.Blob.CloudBlobDirectory cloudBlobDirectory;
    Microsoft.WindowsAzure.Storage.Blob.CloudBlobClient    cloudBlobClient;
    Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer cloudBlobContainer;
    Microsoft.WindowsAzure.Storage.CloudStorageAccount     cloudStorageAccount;
    Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob     blob;
    str containerName, directoryName, fileName;
    Description1000 connectionStr;
    boolean isDeleteHistoryFile, isHeaderWrite;
    str header;
    str lines;
    str downloadUrl;


    /// <summary>
    /// Connection String
    /// </summary>
    /// <param name = "_connectionStr"></param>
    /// <returns></returns>
    public Description1000 parmConnectionStr(Description1000 _connectionStr = connectionStr)
    {
        connectionStr = _connectionStr;

        return connectionStr;
    }

    /// <summary>
    /// Ana klasör yolu. Örneğin: "url/folder"
    /// </summary>
    /// <param name = "_name"></param>
    /// <returns></returns>
    public str parmContainerName(str _name = containerName)
    {
        containerName = _name;

        return containerName;
    }

    /// <summary>
    /// Alt klasör yolu. Örneğin: "url/folder/Hazine"
    /// </summary>
    /// <param name = "_name"></param>
    /// <returns></returns>
    public str parmDirectoryName(str _name = directoryName)
    {
        directoryName = _name;

        return directoryName;
    }

    /// <summary>
    /// Dosya adı. Örneğin: "Test.csv"
    /// </summary>
    /// <param name = "_name"></param>
    /// <returns></returns>
    public str parmFileName(str _name = fileName)
    {
        fileName = _name;

        return fileName;
    }

    /// <summary>
    /// Aynı isimle daha önceden oluşan dosya silinsin mi ? Yes = siler. No = Silmez.
    /// </summary>
    /// <param name = "ret"></param>
    /// <returns></returns>
    public boolean parmIsDeleteHistoryFile(boolean ret = isDeleteHistoryFile)
    {
        isDeleteHistoryFile = ret;

        return isDeleteHistoryFile;
    }

    /// <summary>
    /// Başlık olarak yazılacak verilerdir. En tepedeki tek satırı temsil eder. Örn: "Ad;Soyad;TcNo";
    /// </summary>
    /// <param name = "_conHeader"></param>
    /// <returns></returns>
    public str parmHeader(str _header = header)
    {
        header = _header;

        return header;
    }

    /// <summary>
    /// Satır olarak yazılacak verilerdir. Örn: "Ali;Veli;4444";
    /// </summary>
    /// <param name = "_conLines"></param>
    /// <returns></returns>
    public str parmLines(str _lines = lines)
    {
        lines = _lines;

        return lines;
    }

    /// <summary>
    /// Başlık var mı ? Yes = Var, No = Yok.
    /// </summary>
    /// <param name = "_ret"></param>
    /// <returns></returns>
    public boolean parmIsHeaderWrite(boolean _ret = isHeaderWrite)
    {
        isHeaderWrite = _ret;

        return isHeaderWrite;
    }

    private boolean validate()
    {
        boolean ret = true;

        if(this.parmConnectionStr() == "")
        {
            ret = ret && checkFailed("Connection string belirtilmeli.");
        }

        if(this.parmContainerName() == "")
        {
            ret = ret && checkFailed("Ana klasör adı belirtilmeli");
        }

        if(this.parmFileName() == "")
        {
            ret = ret && checkFailed("Dosya adı belirtilmeli. Örn: Test.csv ya da test.txt gibi.");
        }

        return ret;
    }

    /// <summary>
    /// Default tanımlar yapılır.
    /// </summary>
    private void init()
    {
        cloudStorageAccount  = Microsoft.WindowsAzure.Storage.CloudStorageAccount::Parse(this.parmConnectionStr());
        cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
    }

    public void write()
    {
        Microsoft.WindowsAzure.Storage.StorageException storageException;
        System.Text.Encoding encoding;
        System.Byte[]        byteArray;

        if(this.validate())
        {
            this.init();

            //url/folder
            cloudBlobContainer  = cloudBlobClient.GetContainerReference(this.parmContainerName());
            //url/folder/csvfiles
            cloudBlobDirectory = cloudBlobContainer.GetDirectoryReference(this.parmDirectoryName());
            //url/folder/csvfiles/SemTest.csv
            blob = cloudBlobDirectory.GetBlockBlobReference(this.parmFileName());

            //Önceden oluşturulmuş kayıt varsa, silinir.
            if(blob && blob.Exists(null, null))
            {
                //Önceki dosya silinsin true ise;
                if(this.parmIsDeleteHistoryFile() == true)
                {
                    blob.Delete(0, null, null, null);
                }
            }

            //Dosya boş olarak oluşturulur.
            blob.UploadFromStream(new System.IO.MemoryStream(), null, null, null);

            try
            {
                //Dosya açılır.
                using (Microsoft.WindowsAzure.Storage.Blob.CloudBlobStream cloudBlodStream = blob.OpenWriteAsync().Result)
                {
                    //Başlık varsa yazılır.
                    if(this.parmIsHeaderWrite() == true)
                    {
                        // + Başlık yazılır;
                        encoding = System.Text.Encoding::GetEncoding(28591);
                        byteArray = encoding.GetBytes(header);

                        cloudBlodStream.Write(byteArray, 0, byteArray.Length);
                        // -
                    }

                    // + Satırlar yazılır.
                    encoding = System.Text.Encoding::GetEncoding(28591);
                    byteArray = encoding.GetBytes(lines);

                    cloudBlodStream.Write(byteArray, 0, byteArray.Length);
                    // -

                    cloudBlodStream.Flush();
                    cloudBlodStream.Close();
                    cloudBlodStream.Dispose();

                    downloadUrl = blob.Uri.ToString();
                }

            }
            catch (storageException)
            {
                if (storageException.RequestInformation.HttpStatusCode != 404)
                {
                    throw Error(storageException.Message);
                }
            }
        }
    }

    public str getDownloadUrl()
    {
        return downloadUrl;
    }

}

Örnek kullanım için bu job’tan yararlanabilirsiniz.

//Semih - Azure blob Storage test
class SemAzureBlobStorageTestJob
{
    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        SmhAzureBlobStorageHelper helper = new SmhAzureBlobStorageHelper();
        container conHeader = conNull();
        container conLines = conNull();
        str header;
        str lines;

        helper.parmConnectionStr("DefaultEndpointsProtocol=https;AccountName=accountNameTest;AccountKey=accountKeyTest;EndpointSuffix=core.windows.net");
        // url/folder
        helper.parmContainerName("folder");
        // url/folder/csvfiles
        helper.parmDirectoryName("csvfiles");
        // url/folder/csvfiles/SemTest.csv
        helper.parmFileName("SemTest.csv");
        // önceden oluşturulan aynı isimde dosya varsa siler.
        helper.parmIsDeleteHistoryFile(true);
        // Başlık verim var.
        helper.parmIsHeaderWrite(true);

        conHeader += ["Ad"];
        conHeader += ["Soyad"];
        conHeader += ["No"];

        header = strFmt("%1\r\n", con2Str(conHeader, ";"));
        // Başlık verileri
        helper.parmHeader(header);

        for(int i=0; i<=10; i++)
        {
            conLines += ["Semih"];
            conLines += ["Çelikol"];
            conLines += [i];

            lines += strFmt("%1\r\n", con2Str(conLines, ";"));

            conLines = conNull();
        }
        // Satırlar
        helper.parmLines(lines);

        // Dosya yazılır
        helper.write();
        
        // Oluşan dosya linki. url/folder/csvfiles/SemTest.csv
        info(helper.getDownloadUrl());
    }

}

Yararlı olabilecek kaynaklar:
https://allaboutdynamic.com/2020/11/03/d365-ax7-read-download-a-file-from-azure-blob-storage-using-x/

https://stackoverflow.com/questions/19161869/append-to-cloudblockblob-stream

https://dynamics2012to365.blogspot.com/2019/07/write-csv-file-on-azure-blob.html

Bir Cevap Yazın