Following a number of tutorials and examples on line I have constructed a file download system built on jQuery on the client and MS WebAPI on the server.
Providing direct links to the files isn't possible as the API requires authentication, thus the file URL is an API endpoint not the file location.
On the server I have this:
[HttpGet]
[Route("download/{filename}")]
public HttpResponseMessage DownloadFile(string filename)
{
try
{
// https://gist.github.com/joeriks/3714093
string path = string.Format("{0}/Exports/{1}", root, filename);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "download.txt";
return result;
}
catch (Exception ex)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
Which returns a response as expected:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 4809
Content-Type: application/octet-stream
Expires: -1
Server: Microsoft-IIS/10.0
Content-Disposition: attachment; filename=download.txt
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: content-Type, accept, origin, X-Requested-With, X-Authentication, X-Nonce, name
Date: Thu, 25 Oct 2018 13:07:25 GMT
With the text content in the response. So far all is as I would hope.
On the client I have the following for handling responses from my API:
// https://stackoverflow.com/a/23797348
let disposition = jqXHR.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
let filename = "scada-download.txt";
let matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
let type = jqXHR.getResponseHeader('Content-Type');
let blob = new Blob([data], { type: "text/csv" });
var downloadUrl = URL.createObjectURL(blob);
let $a = $("<a id='temp_download_link' style='display: none;' />").attr("href", downloadUrl).attr("download", filename);
$("body").append($a);
$a.trigger("click");
}
This does as advertised, and adds an anchor to the page, and clicks it triggering the download.
The download works, and saves a file with the correct contents.
The only bit that doesn't work is that in both browsers I've tested it in (Chrome 69, FF: 62) the default file name provided is simply a GUID.
The api and the client code are both running from my local dev machine at present, http://127.0.0.1:9000/[client | api]
so cross origin shouldn't be playing any part.
The call to the api is made via ajax. Ultimately the jQuery $.ajax() method
For clarity sake, the anchor inserted into the DOM is:
<a id="temp_download_link" style="display: none;" href="blob:http://127.0.0.1:9000/c2c5ffb5-3f22-4a57-8775-4e0bbfbfef9e" download="download.txt"></a>
The default filename provided by Chrome is the GUID in the URL, FF generates a seemingly unconnected 6 character random string.
Specifically,
Why are the browsers ignoring both the download="download.txt"
attribute of the anchor and the Content-Disposition: attachment; filename=download.txt
?
UPDATE:
I forked this fiddle:
And added some other values for the href attribute, it seems that the whole setting the filename is flaky at best:
UPDATE 2
I've copied the link from a working example into my fiddle, it works from the original site, but not from fiddle.
http://jsfiddle.net/yubjqwvs/2/
I have a feeling that the answer will boil down to "Why does it work on David Walsh Blog, but not on fiddle?"