Reputation: 1567
For file downloading from an ASP.NET Core 2 application I’ve been using the PhysicalFileResult
(an implementation of IActionResult
) to return files from a controller GET
action. For filenames with 'typical' characters everything works fine. This includes the accented and Kanji character sets that I've tested thus far.
However, if the filename contains an Emoji (something allowed by the Windows file system) the resulting filename is malformed or miss-encoded.
For example I'd expect the following code to deliver a file named 😀.png
, instead it delivers a file named: ��.png
.
public async Task<IActionResult> GetByIdAsync([FromRoute] long id)
{
return PhysicalFile("c:\\file.png", "image/png", "😀.png");
}
Is there a particular way that filenames containing Emojis need to be encoded in order for the filename to be preserved, or is this an issue with PhysicalFileResult
?
Upvotes: 4
Views: 794
Reputation: 26494
The reality is that your issue is related to the mess that is the Content-Disposition
header and more generally character encoding in HTTP headers. There is a very old Stack Overflow question: How to encode the filename parameter of Content-Disposition header in HTTP whose answers while dated cover a lot of what your issue is, but lets break it down.
The PhysicalFileResult
inherits from FileResult
and when being prepared for return to the client is processed by the FileResultExecutorBase
and the code you are interested from that class is contained in the SetContentDispositionHeader
method:
private static void SetContentDispositionHeader(ActionContext context, FileResult result)
{
if (!string.IsNullOrEmpty(result.FileDownloadName))
{
// From RFC 2183, Sec. 2.3:
// The sender may want to suggest a filename to be used if the entity is
// detached and stored in a separate file. If the receiving MUA writes
// the entity to a file, the suggested filename should be used as a
// basis for the actual filename, where possible.
var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName(result.FileDownloadName);
context.HttpContext.Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
}
}
Which ultimately lands you in the class ContentDispositionHeaderValue
from the Microsoft.Net.Http.Headers
namespace. There are currently 725 lines of code in this class most of which are there to ensure that the values returned are valid given the complicated nature of HTTP response headers.
RFC7230 specified that in the headers there is no useful encoding besides ASCII:
Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets. A recipient SHOULD treat other octets in field content (obs-text) as opaque data.
Ultimately, some relief may be in sight as RFC8187 from September 2017 provides clarification which should alleviate some of this mess. Issue 2688 filed against HTTP Abstractions covers the need to incorporate this RFC into ASP.Net.
In the meantime you can try slugging an appropriately http encoded filename into the URI (http://example.com/filedownload/33/%F0%9F%98%80.png
) rather than relying upon the flawed Content-Disposition
header for filename suggestion. To be clear this is an incomplete fix and has the potential for not working on all clients.
Upvotes: 2