How to Retrieve High-Resolution Images in Dynamics 365 Plugins & Code Activities
Master the techniques for accessing full-size image data instead of thumbnail versions when working with Dataverse image columns in plugins and custom workflow activities.

A seasoned Techno-Functional Professional with 7+ years of experience in Information Technology (IT), specializing in Microsoft Dynamics 365 Customer Engagement (CE) and Power Platform Solutions. I bring extensive expertise in out-of-the-box and custom configurations, customizations, integrations and data migrations. My skill set includes proficiency in working with Model Driven Apps, Canvas Apps, Power Pages (formerly Power Portals), Power Automate, Approval flows, Code Activities, Plugins, Workflows, Business Rules, JavaScript, HTML Web Resources, PCF Controls, Chatbots, SSRS Reports, Microsoft Copilot, Azure OpenAI, Azure Web Services, Azure Functions, Azure Logic Apps, Blazor Web Assemblies, C# and requirement analysis.
When developing Plugins or Custom Workflow Activities in Dynamics 365 CE or PowerApps (Dataverse), a common roadblock is retrieving image fields. If you access an image attribute using standard Retrieve or RetrieveMultiple(FetchXML) logic, Dataverse returns a low-resolution thumbnail by default.
To get the original, full-resolution image, you must use the file streaming message requests. This post provides a clean, reusable approach to extracting full image data as either a Base64 string or raw byte[] data.
The Problem: The "Thumbnail Trap"
In Dataverse, image fields are optimized for performance. When you call entity.GetAttributeValue<byte[]>("your_field"), the platform serves a compressed version to save bandwidth. For high-quality email signatures, document generation, or external integrations, this thumbnail is usually too blurry to use.
The Solution: Multi-Block Downloading
To bypass compression, you must treat the image as a file and download it in "blocks" (chunks). This ensures you pull the raw, uncompressed data directly from the storage layer.
The Implementation
Copy the following methods into your helper class. This logic is sandbox-safe and designed for high performance within the 2-minute plugin execution limit.
(Note: Ensure you have Microsoft.Crm.Sdk.Messages included in your using statements).
1. The Entry Function: GetFullImageContent
This is the method you will call from your main logic. It allows you to toggle the return format using a Boolean parameter.
/// <summary>
/// Retrieves full-resolution image data from a record.
/// </summary>
/// <param name="service">The IOrganizationService instance.</param>
/// <param name="entityName">Logical name of the entity (e.g., "account").</param>
/// <param name="recordId">The GUID of the specific record.</param>
/// <param name="imageFieldName">The logical name of the image attribute.</param>
/// <param name="asBase64">Set to true for Base64 string, false for byte array string.</param>
/// <returns>A string containing the image data.</returns>
public string GetFullImageContent(IOrganizationService service, string entityName, Guid recordId, string imageFieldName, bool asBase64)
{
if (recordId == Guid.Empty || string.IsNullOrEmpty(entityName) || string.IsNullOrEmpty(imageFieldName))
return string.Empty;
// Create the reference to the target record
EntityReference recordRef = new EntityReference(entityName, recordId);
// Call the helper to stream the full bytes
byte[] imageBytes = DownloadFullImage(service, recordRef, imageFieldName);
if (imageBytes == null || imageBytes.Length == 0)
return string.Empty;
// Return the format requested by the caller
return asBase64 ? Convert.ToBase64String(imageBytes) : BitConverter.ToString(imageBytes);
}
2. The Internal Streaming Helper: DownloadFullImage
This method manages the initialization and the loop required to piece the image chunks back together.
private byte[] DownloadFullImage(IOrganizationService service, EntityReference recordRef, string attributeName)
{
// 1. Initialize the download session
var initializeRequest = new InitializeFileBlocksDownloadRequest()
{
Target = recordRef,
FileAttributeName = attributeName
};
var initializeResponse = (InitializeFileBlocksDownloadResponse)service.Execute(initializeRequest);
string fileToken = initializeResponse.FileContinuationToken;
long fileSize = initializeResponse.FileSizeInBytes;
List<byte> fullFile = new List<byte>((int)fileSize);
long offset = 0;
long blockSize = 4 * 1024 * 1024; // 4 MB Chunk Size
// 2. Loop through and download blocks until complete
while (offset < fileSize)
{
var downloadBlockRequest = new DownloadBlockRequest()
{
BlockLength = blockSize,
FileContinuationToken = fileToken,
Offset = offset
};
var downloadBlockResponse = (DownloadBlockResponse)service.Execute(downloadBlockRequest);
fullFile.AddRange(downloadBlockResponse.Data);
// Move the offset forward by the actual amount of data received
offset += downloadBlockResponse.Data.Length;
}
return fullFile.ToArray();
}
Key Technical Considerations
Enable "Store Full Image": This code will only return the high-resolution file if the "Store full image" option is enabled in the column's definition within the Power Apps Maker Portal.
SDK Versions: Ensure your project is referencing
Microsoft.CrmSdk.CoreAssemblies(v9.0.2.x or higher) to access theInitializeFileBlocksDownloadRequestandDownloadBlockRequestclasses.Performance: While 4MB is the standard block size, the code will automatically handle smaller files in a single pass.
Base64 Use Case: Use
asBase64 = trueif you are injecting the image into an HTML template or returning it via a Web API.Error Handling: In a production environment, always wrap these calls in a
try-catchblock and use theITracingServiceto monitor thefileSizeandoffsetduring execution.
Summary
By ensuring your Dataverse columns are set to Store full image and using the Initialize/Download pattern instead of direct attribute access, you can ensure your Dynamics 365 solutions handle images with the highest possible fidelity.
Happy coding, Wizards!





