[Windows] Fix FilePicker Returns wrong ContentType for *.webp files#31913
[Windows] Fix FilePicker Returns wrong ContentType for *.webp files#31913devanathan-vaithiyanathan wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
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 |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
f2ede30 to
970c082
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 31913Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 31913" |
@kubaflo , I have resolved the conflicts |
|
/review -b feature/refactor-copilot-yml -p windows |
|
/review -b feature/refactor-copilot-yml |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
@kubaflo , Based on AI suggestion, I have modified the fix |
|
/review -b feature/refactor-copilot-yml -p windows |
This comment has been minimized.
This comment has been minimized.
kubaflo
left a comment
There was a problem hiding this comment.
Could you pleas check the ai's suggestions?
@kubaflo , I have addressed the AI suggestion |
|
/review -b feature/enhanced-reviewer |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
@kubaflo , Based on AI suggestion, I have modified the fix. |
|
/review -b feature/enhanced-reviewer |
|
/review -b feature/enhanced-reviewer -p windows |
MauiBot
left a comment
There was a problem hiding this comment.
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.
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 |
🔴 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.ymlsrc/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(...)returningapplication/octet-streamfor.webpon Windows; expectedimage/webp. - PR changes Windows
FileBase(IStorageFile)to preserve specificStorageFile.ContentTypevalues but use a fallback map for blank or generic values. - PR adds Windows Essentials device tests, but they construct
FileResultfrom a path instead of theIStorageFilepath 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-124exercisesnew FileResult(filePath), notnew 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
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
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.
Output Screenshot
Before-fix.mp4
After-fix.mp4