Skip to content

[Windows] Fix FilePicker Returns wrong ContentType for *.webp files#31913

Open
devanathan-vaithiyanathan wants to merge 8 commits into
dotnet:mainfrom
devanathan-vaithiyanathan:fix-31808
Open

[Windows] Fix FilePicker Returns wrong ContentType for *.webp files#31913
devanathan-vaithiyanathan wants to merge 8 commits into
dotnet:mainfrom
devanathan-vaithiyanathan:fix-31808

Conversation

@devanathan-vaithiyanathan

@devanathan-vaithiyanathan devanathan-vaithiyanathan commented Oct 8, 2025

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Issue Details

On Windows, the StorageFile.ContentType API often returns a generic or incorrect MIME type (e.g., .webp → application/octet-stream), causing FilePicker to report wrong or empty ContentType values.

Description of Change

Fixed by adding an extension-to-MIME mapping dictionary in FileSystem.uwp.cs that includes .webp and other formats. The solution uses fallback logic - it preserves Windows' native ContentType when correct, but automatically uses our mapping when Windows returns the generic "application/octet-stream" or empty values.

Issues Fixed

Fixes #31808

Tested the behavior in the following platforms.

  • Windows
  • Mac

Output Screenshot

Before After
Windows
Before-fix.mp4
Windows
After-fix.mp4

@dotnet-policy-service dotnet-policy-service Bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Oct 8, 2025
Comment thread src/Essentials/src/FileSystem/FileSystem.windows.cs
Comment thread src/Essentials/src/FileSystem/FileSystem.uwp.cs Outdated
Comment thread src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs Outdated
Comment thread src/Essentials/src/FileSystem/FileSystem.uwp.cs Outdated
@devanathan-vaithiyanathan devanathan-vaithiyanathan marked this pull request as ready for review October 13, 2025 04:17
Copilot AI review requested due to automatic review settings October 13, 2025 04:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a bug in the Windows implementation of FilePicker where incorrect ContentType values were returned for various file formats, particularly .webp files which were incorrectly reported as "application/octet-stream" instead of "image/webp".

  • Adds comprehensive extension-to-MIME type mapping dictionary as fallback when Windows StorageFile.ContentType API returns generic or incorrect values
  • Implements smart fallback logic that preserves Windows' native ContentType when correct but uses the mapping for problematic cases
  • Includes support for compound extensions like .tar.gz and handles case-insensitive matching with whitespace trimming

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Essentials/src/FileSystem/FileSystem.uwp.cs Core implementation with MIME type mapping dictionary and fallback logic in FileBase constructor
src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs New Windows-specific test cases validating correct ContentType detection for various file extensions

Comment thread src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs Outdated
Comment thread src/Essentials/src/FileSystem/FileSystem.windows.cs
Comment thread src/Essentials/src/FileSystem/FileSystem.windows.cs
@jsuarezruiz

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please resolve conflicts?

@github-actions

github-actions Bot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 31913

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 31913"

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Could you please resolve conflicts?

@kubaflo , I have resolved the conflicts

@MauiBot MauiBot added s/agent-review-incomplete s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) s/agent-changes-requested AI agent recommends changes - found a better alternative or issues and removed s/agent-review-incomplete labels Mar 24, 2026
@dotnet dotnet deleted a comment from MauiBot May 24, 2026
@dotnet dotnet deleted a comment from MauiBot May 24, 2026
@kubaflo

kubaflo commented May 24, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/refactor-copilot-yml -p windows

@kubaflo

kubaflo commented May 24, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/refactor-copilot-yml

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-changes-requested AI agent recommends changes - found a better alternative or issues label May 24, 2026
MauiBot

This comment was marked as outdated.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the ai's suggestions?

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Could you please check the ai's suggestions?

@kubaflo , Based on AI suggestion, I have modified the fix

@kubaflo

kubaflo commented May 29, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/refactor-copilot-yml -p windows

MauiBot

This comment was marked as outdated.

@MauiBot

This comment has been minimized.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you pleas check the ai's suggestions?

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Could you pleas check the ai's suggestions?

@kubaflo , I have addressed the AI suggestion

@kubaflo

kubaflo commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

Comment thread src/Essentials/src/FileSystem/FileSystem.windows.cs Outdated
Comment thread src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs
MauiBot

This comment was marked as outdated.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the ai's suggestions?

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Could you please check the ai's suggestions?

@kubaflo , Based on AI suggestion, I have modified the fix.

@kubaflo

kubaflo commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

MauiBot

This comment was marked as outdated.

@kubaflo

kubaflo commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer -p windows

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@devanathan-vaithiyanathan — new AI review results are available based on this last commit: 448e1a0.
Revert "AI Summary added" To request a fresh review after new comments or commits, comment /review rerun.

Gate Unknown Code Review Needs Changes Confidence Medium Platform Windows

Review Sessions — click to expand
Gate — Test Before & After Fix

Gate Result: ⚠️ ENV ERROR

Platform: WINDOWS · Base: main · Merge base: e904e900

Test Without Fix (expect FAIL) With Fix (expect PASS)
📱 FileSystem_Tests (EnsureFileResultContentType) Category=FileSystem ⚠️ ENV ERROR ⚠️ ENV ERROR
🔴 Without fix — 📱 FileSystem_Tests (EnsureFileResultContentType): ⚠️ ENV ERROR · 174s

No log file found

🟢 With fix — 📱 FileSystem_Tests (EnsureFileResultContentType): ⚠️ ENV ERROR · 173s

No log file found

⚠️ Failure Details

  • ⚠️ FileSystem_Tests (EnsureFileResultContentType) without fix: Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
  • ⚠️ FileSystem_Tests (EnsureFileResultContentType) with fix: Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Int32".
📁 Fix files reverted (2 files)
  • eng/pipelines/ci-copilot.yml
  • src/Essentials/src/FileSystem/FileSystem.windows.cs

UI Tests — Essentials

Detected UI test categories: Essentials

Deep UI tests — 1 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Essentials 1/1 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

Pre-Flight — Context & Validation

Issue: #31808 - MAUI: Windows: FilePicker: Returns wrong ContentType for *.webp files: Returns app/octet-stream.
PR: #31913 - [Windows] Fix FilePicker Returns wrong ContentType for *.webp files
Platforms Affected: Windows
Files Changed: 1 implementation, 1 test

Key Findings

  • Issue reports FilePicker.Default.PickMultipleAsync(...) returning application/octet-stream for .webp on Windows; expected image/webp.
  • PR changes Windows FileBase(IStorageFile) to preserve specific StorageFile.ContentType values but use a fallback map for blank or generic values.
  • PR adds Windows Essentials device tests, but they construct FileResult from a path instead of the IStorageFile path used by FilePicker.
  • GitHub CLI was unauthenticated, so context was gathered from the local squashed PR branch plus unauthenticated public GitHub API/web content.

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: medium
Errors: 0 | Warnings: 1 | Suggestions: 0

Key code review findings:

  • src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs:119-124 exercises new FileResult(filePath), not new FileResult(IStorageFile), so it can miss the FilePicker regression path.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31913 Broad Windows extension-to-MIME FrozenDictionary, used when StorageFile.ContentType is blank or application/octet-stream; path-based Windows tests for several extensions. FAIL (Gate pre-run) src/Essentials/src/FileSystem/FileSystem.windows.cs, src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs Gate result supplied by caller; not re-run.

Code Review — Deep Analysis

Code Review PR #31913

Independent Assessment

What this changes: Adds a Windows-only extension-to-MIME fallback for FileBase.ContentType, especially when StorageFile.ContentType is empty or application/octet-stream. Adds Windows device tests for mapped extensions/casing/whitespace.
Inferred motivation: Fix Windows returning generic content type for files like .webp, likely from FilePicker/FileResult.

Reconciliation with PR Narrative

Author claims: Adds deterministic extension-to-MIME mapping including .webp; preserves Windows native content type when it is specific and falls back for empty or generic values.
Agreement/disagreement: The implementation matches the broad fallback claim, but the added tests primarily exercise new FileResult(filePath) rather than the FilePicker/IStorageFile constructor path where the bug occurs.

Findings

Warning Test does not exercise the FilePicker/StorageFile regression path

The production bug appears to be the internal Windows FileResult(IStorageFile) path where StorageFile.ContentType may be application/octet-stream. The new test at src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs:119-124 constructs new FileResult(filePath), which exercises PlatformGetContentType() via the public path-based constructor, but not the new constructor fallback at FileSystem.windows.cs:158-168.

Concrete risk: if the IStorageFile fallback regresses while PlatformGetContentType(".webp") still works, this test remains green but Windows FilePicker can return application/octet-stream again. If feasible, add coverage that goes through StorageFile.GetFileFromPathAsync(...)/FileResult(IStorageFile) or another non-interactive equivalent of the FilePicker path.

Devil's Advocate

The implementation itself looks safe: it preserves Windows-provided specific content types and only overrides empty/generic values. The hardcoded map is broader than required for .webp, but it is Windows-scoped and only used as fallback, so compatibility risk is low.

Verdict: LGTM / NEEDS_DISCUSSION

Confidence: medium
Summary: The code approach appears correct and safe for Windows ContentType semantics. The main gap is that the added regression test does not directly cover the StorageFile/FilePicker path that motivated the fix.


Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Surgical Windows .webp fallback in FileBase(IStorageFile) plus StorageFile-path test. FAIL 2 files Build FAIL because test referenced Windows.Storage.StorageFile without a global alias inside a Microsoft namespace.
2 try-fix Narrow reusable FileSystemUtils fallback helper for .webp/.tar.gz, used by constructor and PlatformGetContentType; StorageFile-path test with global alias. FAIL 3 files Build FAIL because WinRT IAsyncOperation<StorageFile> await needed using System; or equivalent await extension scope.
3 try-fix Treat generic StorageFile.ContentType as unset so shared GetContentType() invokes Windows PlatformGetContentType; only .webp fallback; focused StorageFile regression test. PASS 2 files Build succeeded; xUnit XML reported 253 total, 239 PASS, 0 FAIL, 14 skipped, including the new StorageFile .webp test. Wrapper exit was a result-summary parser issue, not a test failure.
PR PR #31913 Broad Windows extension-to-MIME map and broad path-based tests. FAIL (Gate pre-run) 2 files Original PR gate was supplied as FAIL; PR tests do not cover IStorageFile regression path.

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer 1 Yes Candidate 1: .webp-only surgical fallback and StorageFile test.
maui-expert-reviewer 2 Yes Candidate 2: narrow reusable helper after learning candidate 1's namespace failure.
maui-expert-reviewer 3 Yes Candidate 3: shared fallback path after learning candidate 2's WinRT await failure.

Exhausted: Yes
Selected Fix: Candidate #3 - It passes the targeted Windows Essentials result set and is smaller/more focused than PR #31913. It fixes the actual FilePicker/IStorageFile .webp path without introducing a broad partial MIME database.


Report — Final Recommendation

Comparative Report - PR #31913

Candidates compared

Rank Candidate Result Assessment
1 try-fix-3 PASS Focused .webp fix for the actual Windows IStorageFile path. It treats application/octet-stream as unset so existing content-type fallback runs, adds only .webp fallback behavior, and includes a direct StorageFile regression test. Recorded Windows Essentials result XML showed 253 total, 239 passed, 0 failed, 14 skipped, with the new .webp StorageFile test passing; the wrapper failure was a summary parser issue.
2 pr-plus-reviewer Not recorded passing Improves the PR by adding the expert reviewer's missing StorageFile regression coverage, but retains the PR's broad partial MIME table and has no recorded passing regression result.
3 pr FAIL The raw PR attempts a broad extension-to-MIME fallback and adds path-based tests, but the supplied gate result failed and the expert reviewer found that its tests miss the original IStorageFile FilePicker path.
4 try-fix-2 FAIL Plausible reusable helper design, but failed to build because the test awaited WinRT IAsyncOperation<StorageFile> without the required System await extension scope.
5 try-fix-1 FAIL Plausible narrow implementation, but failed to build because the test referenced Windows.Storage.StorageFile from inside a Microsoft.* namespace without a global:: alias.

Expert review influence

The expert reviewer produced one actionable inline finding against src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs:123: the PR test constructs FileResult from a file path instead of IStorageFile, so it can pass while the FilePicker regression remains uncovered. The pr-plus-reviewer sandbox candidate applies that feedback by adding a direct global::Windows.Storage.StorageFile test.

Winner

Winner: try-fix-3

try-fix-3 is the only candidate with recorded passing regression evidence, and candidates that failed regression tests must rank lower than candidates that passed. It is also the most focused fix: it covers the actual Windows FilePicker/IStorageFile .webp path without adding a broad, incomplete MIME-type database or unrelated extension behavior.


Future Action — alternative fix proposed (try-fix-3)

Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-3 as the strongest fix.

Why: try-fix-3 won because it is the only candidate with recorded passing Windows Essentials regression results and it directly covers the FilePicker/IStorageFile .webp path. It is also narrower than the PR fix, avoiding a broad partial MIME database while addressing the reported Windows fallback behavior.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (try-fix-3)
diff --git a/src/Essentials/src/FileSystem/FileSystem.windows.cs b/src/Essentials/src/FileSystem/FileSystem.windows.cs
index d3484157da..8e755ca449 100644
--- a/src/Essentials/src/FileSystem/FileSystem.windows.cs
+++ b/src/Essentials/src/FileSystem/FileSystem.windows.cs
@@ -88,7 +88,9 @@ namespace Microsoft.Maui.Storage
 			: this(file?.Path)
 		{
 			File = file;
-			ContentType = file?.ContentType;
+			ContentType = string.Equals(file?.ContentType, DefaultContentType, StringComparison.OrdinalIgnoreCase)
+				? null
+				: file?.ContentType;
 		}
  
 		void PlatformInit(FileBase file)
@@ -98,8 +100,13 @@ namespace Microsoft.Maui.Storage
  
 		internal IStorageFile File { get; set; }
  
-		// we can't do anything here, but Windows will take care of it
-		string PlatformGetContentType(string extension) => null;
+		string PlatformGetContentType(string extension)
+		{
+			if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase))
+				return "image/webp";
+
+			return null;
+		}
  
 		internal async virtual Task<Stream> PlatformOpenReadAsync()
 		{
diff --git a/src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs b/src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs
index 4a1a068d6b..c255151732 100644
--- a/src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs
+++ b/src/Essentials/test/DeviceTests/Tests/FileSystem_Tests.cs
@@ -1,7 +1,11 @@
+using System;
 using System.IO;
 using System.Threading.Tasks;
 using Microsoft.Maui.Storage;
 using Xunit;
+#if WINDOWS
+using StorageFile = global::Windows.Storage.StorageFile;
+#endif
  
 namespace Microsoft.Maui.Essentials.DeviceTests
 {
@@ -103,26 +107,25 @@ namespace Microsoft.Maui.Essentials.DeviceTests
 		}
  
 #if WINDOWS
-		[Theory]
-		[InlineData(".webp", "image/webp")]
-		[InlineData(".jpg", "image/jpeg")]
-		[InlineData(".JPG", "image/jpeg")]
-		[InlineData(".Jpg", "image/jpeg")]
-		[InlineData(".jPg", "image/jpeg")]
-		[InlineData(".jpg ", "image/jpeg")]  // Trailing space
-		[InlineData(" .jpg", "image/jpeg")]  // Leading space
-		[InlineData(" .jpg ", "image/jpeg")] // Leading and trailing spaces
-		[InlineData(".png", "image/png")]
-		[InlineData(".PNG", "image/png")]
-		[InlineData(".tar.gz", "application/gzip")]
-		[InlineData(".TAR.GZ", "application/gzip")]
-		public async Task EnsureFileResultContentType(string extension, string expectedMimeType)
+		[Fact]
+		public async Task FileResult_FromStorageFile_WebpContentType_IsImageWebp()
 		{
-			string filePath = Path.Combine(FileSystem.CacheDirectory, $"test{extension}");
-			await File.WriteAllTextAsync(filePath, $"File Content type is {expectedMimeType}");
-			FileResult fileResult = new FileResult(filePath);
-			Assert.Equal(expectedMimeType, fileResult.ContentType);
-			File.Delete(filePath);
+			string filePath = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():N}.webp");
+
+			try
+			{
+				await File.WriteAllTextAsync(filePath, "webp content");
+
+				var storageFile = await StorageFile.GetFileFromPathAsync(filePath);
+				FileResult fileResult = new FileResult(storageFile);
+
+				Assert.Equal("image/webp", fileResult.ContentType);
+			}
+			finally
+			{
+				if (File.Exists(filePath))
+					File.Delete(filePath);
+			}
 		}
 #endif
 	}

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the ai's suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/windows s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MAUI: Windows: FilePicker: Returns wrong ContentType for *.webp files: Returns app/octet-stream.

7 participants