Introduction

The limitations of default attachment storage mechanisms in Dynamics CRM can pose obstacles for organizations dealing with substantial amounts of data. In this article, we explore the inherent limitations of Dynamics CRM in handling large files and delve into the strategy of storing these files as multiple parts to overcome these hurdles.

Change File Size Limit for Attachments

The Dynamics 365 system administrator can change the maximum size of attachments from 0 to 128 MB (131,072 KB) in the System Settings dialog box.

Implementation Steps:

  1. Navigate to https://make.powerapps.com
  2. Click the Gear Icon at the Top --> Select Advance Settings.
  3. It will open Settings Page --> Select Administration.

4. Click System Settings.

5. Once the page loaded select the Email tab.

Here you will find a section called Set file size limit for attachments in which you can set the maximum file size for attachments in kilobytes. The default size limit for attachments is 5120kB/5MB but can be increased to 32768Kb/32MB for older versions of CRM and 131072Kb/128MB for newer versions of CRM.

Now, let's explore the maximum size the system will allow.

Within the Dataverse Environment, only values ranging from 0 to 131,072 are permitted.

Splitting Files into Multiple Parts

However, 128 MB (131,072 KB) is insufficient. Consequently, we can overcome this limitation by dividing our file into smaller parts. This process is relatively straightforward and serves as a common workaround for larger files.

To divide a file into parts, you can use the following code:

static List<byte[]> DivideFile(byte[] data, int chunkSize)
{
    List<byte[]> fileParts = new List<byte[]>();

    int offset = 0;
    while (offset < data.Length)
    {
        int remaining = data.Length - offset;
        int bufferSize = Math.Min(chunkSize, remaining);

        byte[] buffer = new byte[bufferSize];
        Buffer.BlockCopy(data, offset, buffer, 0, bufferSize);

        fileParts.Add(buffer);
        offset += bufferSize;
    }
    return fileParts;
}

When specifying the 'chunkSize' as the part size limit in bytes, it should be set to a value lower than the attachments limit. You can verify this using the following code:

public int GetAttachmentSizeLimit()
        {
            QueryExpression query = new QueryExpression("organization")
            {
                ColumnSet = new ColumnSet("maxuploadfilesize")
            };
            return Service.RetrieveMultiple(query).Entities
                .FirstOrDefault().GetAttributeValue<int>("maxuploadfilesize");
        }

Next, you should save a portion of the data in separate attachments and associate the relevant information. The following details are required:

 

File information:

Name

Type

Description

FileId

Guid

File identifier

FileTotalParts

int

Total number of parts

 

Parts information:

Name

Type

Description

FilePartId

Guid

File part identifier

FilePartNumber

int

Part sequence number

Each section should be assigned a sequence number, FilePartNumber, ranging from 0 to the specified value of the corresponding configuration parameter minus 1.

public class FileInfo
{
    public Guid FileId { get; set; }
    public int FileTotalParts { get; set; }
    public List<FilePartInfo> FileParts { get; set; }
}

public class FilePartInfo
{
    public Guid FilePartId { get; set; }
    public int FilePartNumber { get; set; }
}
Example of creating annotations:
List<byte[]> fileParts = DivideFile(data, chunkSize);

Entity fileInfoRecord = new Entity("FileInfoEntity");
fileInfoRecord.Id = _service.Create(fileInfoRecord);

FileInfo fileInfo = new FileInfo()
{
    FileId = fileInfoRecord.Id,
    FileTotalParts = fileParts.Count,
    FileParts = new List<FilePartInfo>()
};

for (int i = 0; i < fileParts.Count; i++)
{
    Entity filePartRecord = new Entity("annotation")
    {
        Attributes =
        {
            { "documentbody", Convert.ToBase64String(fileParts[i]) },
            { "subject", "File subject" },
            { "filename", $"FileName_part{i}.bin" },
            { "objectid", fileInfoRecord.ToEntityReference() },
        }
    };

    filePartRecord.Id = _service.Create(filePartRecord);

    fileInfo.FileParts.Add(
        new FilePartInfo() { FilePartId = filePartRecord.Id, FilePartNumber = i }
    );
}

fileInfoRecord["JSON"] = JsonConvert.SerializeObject(fileInfo);
_service.Update(fileInfoRecord);

Joining Split Files

To join a file from parts, you can utilize the following code:

static byte[] JoinFiles(List<byte[]> fileParts)
{
    int totalSize = fileParts.Sum(part => part.Length);
    byte[] joinedData = new byte[totalSize];

    int offset = 0;
    foreach (byte[] partData in fileParts)
    {
        Buffer.BlockCopy(partData, 0, joinedData, offset, partData.Length);
        offset += partData.Length;
    }
    return joinedData;
}

Example of joining file parts:

Entity fileInfoRecord = _service.Retrieve("FileInfoEntity", fileId, new ColumnSet(true));
FileInfo fileInfo = JsonConvert.DeserializeObject<FileInfo>(
    fileInfoRecord.GetAttributeValue<string>("JSON")
);

QueryExpression query = new QueryExpression("annotation")
{
    ColumnSet = new ColumnSet(true),
    Criteria =
    {
        Conditions =
        {
            new ConditionExpression("objectid", ConditionOperator.Equal, fileInfoRecord.Id)
        }
    }
};

DataCollection<Entity> fileParts = _service.RetrieveMultiple(query).Entities;

List<byte[]> sorteredFileParts = new List<byte[]>();

for (int i = 0; i < fileInfo.FileTotalParts; i++)
{
    sorteredFileParts.Add(
        Convert.FromBase64String(
            fileParts
                .First(x => x.Id == fileInfo.FileParts.First(y => y.FilePartNumber == i).FilePartId)
                .GetAliasedValue<string>("documentbody")
        )
    );
}

byte[] fileData = JoinFiles(sorteredFileParts);

Conclusion

In summary, this article addresses Dynamics CRM's challenges with large files and proposes a solution: breaking files into manageable parts stored as attachments. The provided implementation steps empower administrators to customize attachment size limits. This strategic approach not only bypasses CRM's default restrictions but also streamlines the reconstruction of the original file. Embracing this technique enhances data handling efficiency, creating a more robust file management system in Dynamics 365. This article advocates for a practical and effective method to maximize Dynamics CRM's capabilities in handling substantial data.