
By Dartmouth College Electron Microscope Facility - Source and public domain notice at Dartmouth College Electron Microscope Facility ([1], [2]), Public Domain, https://commons.wikimedia.org/w/index.php?curid=24407
https://en.wikipedia.org/wiki/Scanning_electron_microscope
See the PYTHON Version of the Article here:
Handling TIFF Images with Python Flask and AJAX
** TIFF?**
Alright, let’s talk about TIFF files. You know, those beefy, high-quality image files that refuse to be compressed like their JPEG cousins?
The ones that scientists, doctors, and other people with fancy lab coats love to use? Yeah, those.
If you’ve ever tried to display a TIFF in a web browser, you’ve probably been met with the digital equivalent of a confused shrug.
Turns out, browsers don’t support TIFFs because they’re too high-maintenance.
The are lossless, which can make them quite large
see
JPEG Compression The Good, the Bad, and the Pixelated
TIFF: The Overachiever
TIFF (Tagged Image File Format) is like that one friend who insists on bringing a 4K projector to movie night.
It’s high quality, lossless, and supports multiple pages, transparency, and all sorts of extras. It’s used in:
- Medical imaging (X-rays, MRIs, fancy scans)
- Scientific imaging (Electron microscopes, astrophotography, things that make you go “whoa”)
- Document scanning (Lawyers and accountants love this stuff)
- Printing and publishing (Because blurry images are for amateurs)
PNG: The Chill One
It’s lightweight, compressed, and supported by literally everything.
It’s perfect for your vacation photos but totally unsuitable if you need pixel-perfect precision.
The Browser’s TIFF Support
Here’s the deal: modern web browsers simply refuse to display TIFFs natively.
Since we can’t make browsers like TIFFs, we need to convert them into PNGs before sending them over.
Test - Example
What’s the code below do? It gives you a Blazor ui allowing you to up load a TIFF file to the sever.
The TIFF is then converted, in memory, and sent back to the Browser as a base64 encoded PNG.
In a real situation the TIFF file likely will come from server file location or a cloud provider .
The point of the code is to demonstrate the TIFF conversion on the fly.
So your mileage may vary…
What the Sample Does:
- User can Upload a TIFF file from Blazor.
- Converts the TIFF to PNG on the server.
- Server Send the PNG (Base64) to clients using SignalR.
- Client Displays the PNG in the Blazor UI.
🖥️ Server-Side (ASP.NET Core + SignalR)
✅ 1. SignalR Hub (Server)
This hub handles sending Base64 PNG images to connected clients.
1
2
3
4
5
6
7
8
9
10
| using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class ImageHub : Hub
{
public async Task SendPngImage(string base64Png)
{
await Clients.All.SendAsync("ReceiveImage", base64Png);
}
}
|
✅ 2. Server-Side Controller to Handle File Upload
This API controller:
- Accepts TIFF file uploads.
- Converts the TIFF image to PNG.
- Encodes PNG as Base64.
- Sends the PNG to clients via SignalR.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
private readonly IHubContext<ImageHub> _hubContext;
public UploadController(IHubContext<ImageHub> hubContext)
{
_hubContext = hubContext;
}
[HttpPost("upload")]
public async Task<IActionResult> UploadTiff(IFormFile file)
{
if (file == null || file.Length == 0)
{
return BadRequest("No file uploaded.");
}
using (MemoryStream inputStream = new MemoryStream())
{
await file.CopyToAsync(inputStream);
byte[] tiffBytes = inputStream.ToArray();
string base64Png = ConvertTiffToPngBase64(tiffBytes);
// Send PNG as Base64 via SignalR
await _hubContext.Clients.All.SendAsync("ReceiveImage", base64Png);
return Ok(new { message = "Image uploaded and converted successfully" });
}
}
private string ConvertTiffToPngBase64(byte[] tiffBytes)
{
using (MemoryStream inputStream = new MemoryStream(tiffBytes))
using (Image image = Image.Load(inputStream)) // Load TIFF
using (MemoryStream outputStream = new MemoryStream())
{
image.Save(outputStream, new PngEncoder()); // Convert to PNG
byte[] pngBytes = outputStream.ToArray();
return Convert.ToBase64String(pngBytes);
}
}
}
|
🌍 Client-Side (Blazor)
✅ 3. Blazor Page (ImageUploader.razor
)
This component:
- Lets users upload a TIFF file.
- Sends the file to the server for conversion.
- Listens for SignalR messages to receive Base64 PNG.
- Displays the converted PNG.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| @page "/upload"
@inject NavigationManager Navigation
@inject Microsoft.AspNetCore.SignalR.Client.HubConnection HubConnection
@inject HttpClient Http
<h3>Upload a TIFF Image</h3>
<InputFile OnChange="UploadFile" />
@if (string.IsNullOrEmpty(base64Image))
{
<p>Waiting for image...</p>
}
else
{
<h3>Converted PNG:</h3>
<img src="@base64Image" alt="Converted PNG" style="max-width: 100%; border: 1px solid #ccc;" />
}
@code {
private string base64Image;
protected override async Task OnInitializedAsync()
{
HubConnection = new Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/imagehub")) // Ensure it matches your server hub
.Build();
// Listen for the "ReceiveImage" event from SignalR
HubConnection.On<string>("ReceiveImage", (base64) =>
{
base64Image = $"data:image/png;base64,{base64}"; // Format Base64 for <img> tag
StateHasChanged(); // Refresh UI
});
await HubConnection.StartAsync();
}
private async Task UploadFile(InputFileChangeEventArgs e)
{
var file = e.File;
if (file == null) return;
var content = new MultipartFormDataContent();
var fileContent = new StreamContent(file.OpenReadStream(file.Size));
content.Add(fileContent, "file", file.Name);
var response = await Http.PostAsync("api/upload/upload", content);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Image uploaded successfully");
}
}
}
|
✅ 4. Register SignalR in Program.cs
Make sure SignalR and the upload controller are registered in Program.cs
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSignalR();
builder.Services.AddControllers(); // Register API controllers
builder.Services.AddHttpClient(); // Needed for HTTP file uploads
var app = builder.Build();
app.MapControllers(); // Map API routes
app.MapHub<ImageHub>("/imagehub");
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
|
Final Notes and Thoughts
✅ Blazor Server fully supports SignalR, making this approach efficient.
✅ Blazor WASM can also work but requires a backend API for image conversion.
✅ Static File Serving allows direct access to uploaded images in /wwwroot/uploads/
, so you could just convert the file and serve it with static file sharing