In this tutorial, I’m going to show you how to use Angular to download a file from an authenticated ASP.NET Web API endpoint.

This is tricky for a couple of reasons:

1. We need to make sure the user is authenticated so anyone else going to the URL won’t have access to the file.
2. JavaScript doesn’t have access to your file system, so you’ll have to rely on the browser to actually download the file.

If you want a user to be able to download a file, give them a file link and the browser will either display it or download it, like this sample PDF.

Simple, right? Here’s the kicker…

We only want the users with access to the secure file to be able to download it. In this case, simply providing a link will not do.

Here are 4 steps to set up a secure file download:

1. Set up a secure endpoint
2. Return a base64 representation of the file instead of the raw file contents
3. Click the link in the browser to fetch the base64 string
4. Place the base64 string into a regular html link

Note: there is one downside to this solution; the user has to click on two separate buttons to download a file.

Here is an example ASP.NET Web API controller endpoint requiring authentication that returns a base64 string of the file contents:

[HttpGet]
[Route("{id}/download")]
[Authorize]
public IHttpActionResult GetSecureFile(string id)
{
    var file = await _db.File.FindAsync(id);
    if (file == null) return NotFound();
    
    // Check if link belongs to the user
    if (file.ApplicationUserId != GetUserId()) return BadRequest("Unauthorized access");

    var bytes = _fileDownloadClient.GetBytes(file);
    var base64 = Convert.ToBase64String(bytes);

    return Ok(base64);
}

Once the endpoint is set up, use the code below to create the first button used to call the ‘getFile()’ function:


Inside of Angular controller fetch the base64 string from the API and assign it to a variable. Notice how we added ‘data:application/pdf;base64,’ to the string:

$scope.getFile = function() {
  FileDownloadService.getFile(id)
    .then(function (data) {
      $scope.pdfFile = "data:application/pdf;base64," + data;
    });
}

Once the file contents are captured, use the HTML5 download attribute:

This attribute, if present, indicates the author intends the hyperlink to be used for downloading a resource so that when the user clicks on the link they will be prompted to save it as a local file. If the attribute has a value, the value will be used as the pre-filled file name in the Save prompt opened when the user clicks on the link

The trick here is embedding the entire contents of the file into the link itself:

Download PDF

Downloading a file is pretty simple, figuring out how to do it securely requires a little more legwork. I originally wrote this post as a point of reference, but figured some other folks out there would find it helpful.

Let me know if you have any questions in the comments below.