← All Posts
Technical · May 5, 2022

Calling an Async Web Service to Stream a Protected Image File

How to display sensitive images to authorized users while preventing browser caching and protecting files at rest — using System.Drawing.Image, MemoryStream, and Base64 encoding.

The Problem

A common challenge in web applications: you need to display an image to the user, but the image is sensitive — it shouldn't be cached by the browser, shouldn't be accessible anonymously, and should be protected at rest on the server. Serving it directly via a static URL solves none of those problems.

The solution is to never serve the file directly. Instead, serve it as a Base64-encoded data URI generated server-side on demand, returned through an authenticated web service call. The browser displays the image without ever receiving a cacheable file reference.

The Three-Step Approach

1

Convert to a System.Drawing.Image Object

Access the protected file on the server and load it into a System.Drawing.Image object. This gives you a .NET representation of the image that you control entirely — the file itself never goes near the HTTP response.

2

Convert to a Byte Array via MemoryStream

Use a MemoryStream inside a using statement to convert the image into a byte array. Save the Drawing.Image in its RawFormat attribute, then call ToArray() on the stream to extract the bytes.

C# — Image to byte array
using (MemoryStream ms = new MemoryStream()) { image.Save(ms, image.RawFormat); byte[] imageBytes = ms.ToArray(); }
3

Encode to Base64 and Return

Convert the byte array to a Base64 string and format it as a data URI. Return this string from your web service. On the client side, assign it directly to an image element's src attribute.

C# — Byte array to Base64 data URI
string base64 = Convert.ToBase64String(imageBytes); string dataUri = string.Format("data:image/jpeg;base64,{0}", base64);
Client-side — Assign to image element
// Called asynchronously from your client script imgElement.src = dataUri;

What This Protects Against

  • Anonymous access — The web service endpoint enforces authentication. There's no public URL for the file itself.
  • Browser caching — A data URI embedded in a page load or injected via script is not cached the same way a static file is. The image exists only in memory for the duration of the session.
  • File exposure at rest — The raw file path is never transmitted to the client. An attacker cannot guess or enumerate file locations.

The Trade-Off: Size

Base64 encoding increases the size of the data by approximately 33%. For large images, this means significantly larger payloads in the HTTP response. This can cause performance issues if executed at runtime without caching, or if the Base64 string is stored in ViewState — which can result in bloated page sizes and slow load times.

For most use cases involving document thumbnails, ID photos, or certification images, the size trade-off is acceptable. For large images or high-volume requests, consider caching the Base64 string server-side with an appropriate expiry tied to the user's session.

Need secure file handling built right?

We build .NET applications for enterprise clients that handle sensitive data every day. Start with a conversation.

Contact Us →