Explain Codes LogoExplain Codes Logo

Download file from an ASP.NET Web API method using AngularJS

javascript
prompt-engineering
download
blob
Alex KataevbyAlex Kataev·Jan 13, 2025
TLDR

To trigger a file download from ASP.NET Web API via AngularJS, we create an API endpoint to stream the file outputting the correct headers. Then, using AngularJS, we invoke the endpoint, generate a Blob from the response data, and simulate a click on a temporary anchor link to initiate the download. This method is as magical and satisfying as those voila moments when your code runs without any errors on the first try!

AngularJS HTTP Call & Download:

app.service('FileAway', ['$http', function ($http) { this.download = function (fileUrl) { // Blob is not just an alien creature in movies. It can download files too! $http.get(fileUrl, { responseType: 'blob' }).success(function (data, status, headers) { var blob = new Blob([data], { type: headers('Content-Type') }); var downloadLink = angular.element('<a></a>'); downloadLink.attr('href', window.URL.createObjectURL(blob)); downloadLink.attr('download', headers('x-filename') || 'PrefetchedFile.ext'); downloadLink[0].click(); }).error(function (data, status) { // Oops! Something went wrong with the file download. Try again if you dare. }); }; }]);

Web API File Stream Endpoint:

public HttpResponseMessage GetFile() { var filePath = HttpContext.Current.Server.MapPath("~/file.ext"); var result = new HttpResponseMessage(HttpStatusCode.OK); var stream = new FileStream(filePath, FileMode.Open); result.Content = new StreamContent(stream); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "file.ext" }; return result; }

This swift solution delivers the essential code required to implement a quick, clean file download, giving less time for errors to creep in and surprise you.

Customizing file types and headers

Different file types will require different values for the MIME Content-Type header. An incorrect MIME type is like a missing semicolon - the headache is just not worth it! Here's a quick mapping:

  • PDF: application/pdf
  • Word: application/msword or application/vnd.openxmlformats-officedocument.wordprocessingml.document for .docx
  • Excel: application/vnd.ms-excel or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for .xlsx
  • Images: image/jpeg for JPEG, image/png for PNG, etc.

Moving further, the Content-Disposition header on the server-side can be modified to set a custom file name for the download:

result.Content.Headers.ContentDisposition.FileName = "CustomFileName.ext";

Lastly, while x-filename may be less conventional, you might also extract the filename from Content-Disposition. Think of this as the "Plan B" approach:

downloadLink.attr('download', headers('Content-Disposition').split(';')[1].split('=')[1]);

Recyclable AngularJS Directives for Downloads

For better recyclability and encapsulation, it's a great idea to build a custom AngularJS directive for downloads. Think of this as creating your own "download button" that you can plug-and-play anywhere throughout your app!

Handling Internet Explorer's Unique Behavior

Remember, Internet Explorer 11 has a unique way of doing things. You can liken it to that one teammate who has a unique "style" of coding. To cater to it, we might need to save the Blob differently using navigator.msSaveBlob:

if (window.navigator.msSaveOrOpenBlob) { // IE chose to be a little bit too unique here! window.navigator.msSaveBlob(blob, 'downloadedFile.ext'); } else { // All other normal browsers go here. Phew! downloadLink[0].click(); }

Improving user experience

Like the extra cheese in a loaded pizza, special touches to enhance UX in the download process are always appreciated:

  • Progress indicators: Show loading animations to inform users about the ongoing download process.
  • Enable/Disable download links: Disable download links during download and enable it once the download completes or fails.
  • Error messages: Show clear, user-friendly error messages in case downloads fail.
  • Direct downloads: For smaller files, quick, direct downloads can be executed using window.open:
window.open(fileUrl);

However, like deciding to refactor the code at the last minute, this method may backfire and open a new tab or window, getting blocked by popup blockers.