rheone
rheone

Reputation: 1567

ASP.NET Core 2 file download with Emojis in the filename creates malformed filename

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

Answers (1)

ahsteele
ahsteele

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

Related Questions