There seems to be an issue where both Chrome and Edge download a file with the incorrect filename when they receive a header with a UTF-8 filename containing an apostrophe. For example, calling this URL: https://localhost:44328/Home/GetDocument?id=2
Which returns this Response Header:
content-disposition: attachment; filename*=UTF-8''Supplier's Notes.txt
Will download a file named "Supplier's Notes.txt" in Firefox but will download a file named "GetDocument.htm" in both Chrome and Edge.
Is this a known browser issue? Is there any way to address this?
Here is some sample MVC code you can use to reproduce the issue:
HomeController.cs
using DownloadNameWithApostrophe.Models;
using System.Text;
using System.Web.Mvc;
namespace DownloadNameWithApostrophe.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new SampleViewModel());
}
public void GetDocument(int id)
{
if (!SampleViewModel.SampleData.ContainsKey(id))
{
return;
}
var attachmentString = "filename*=UTF-8''" SampleViewModel.SampleData[id];
this.HttpContext.Response.Clear();
this.HttpContext.Response.AddHeader("Content-Disposition", "attachment; " attachmentString);
this.HttpContext.Response.AddHeader("Contenty-type", "application/octet-stream");
var bytes = Encoding.ASCII.GetBytes("Hello World");
this.HttpContext.Response.OutputStream.Write(bytes, 0, bytes.Length);
this.HttpContext.Response.Flush();
this.HttpContext.Response.End();
}
}
}
SampleViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace DownloadNameWithApostrophe.Models
{
public class SampleViewModel
{
public static Dictionary<int, string> SampleData
{
get
{
var downloads = new Dictionary<int, string>();
downloads.Add(1, "Initial Estimate.txt");
downloads.Add(2, "Supplier's Notes.txt");
return downloads;
}
}
[Display(Name = "Available Downloads")]
public Dictionary<int, string> Downloads { get; set; }
public SampleViewModel()
{
Downloads = SampleData;
}
}
}
Index.cshtml
@model DownloadNameWithApostrophe.Models.SampleViewModel
@{
Layout = null;
}
<div >
<div >
<h2>Getting started</h2>
@Html.LabelFor(m => m.Downloads)
@foreach (var download in Model.Downloads)
{
<br />
<a href="@Url.RouteUrl(new {action="GetDocument", controller="Home"})[email protected]">@download.Value</a>
}
</div>
</div>
CodePudding user response:
That's because Chromium implements RFC 3986, in which '
is a reserved character which must be percent-encoded.
You can use Uri.EscapeDataString()
to escape the string:
var attachmentString = "filename*=UTF-8''" Uri.EscapeDataString(SampleViewModel.SampleData[id]);
Then the file name will be "Supplier's Notes.txt" in Chrome and Edge.