← Back to Articles         
File Upload and Download: Angular + ASP.NET Core Web API

File Upload and Download: Angular + ASP.NET Core Web API

Published on 01 Oct 2026     10 min read Angular
HTTP & Web API Integration

File Upload and Download: Angular + ASP.NET Core Web API

For files, Angular and ASP.NET Core usually use two different HTTP patterns:

UPLOAD
Angular File
   ↓
FormData
   ↓
multipart/form-data
   ↓
ASP.NET Core
   ↓
IFormFile


DOWNLOAD
ASP.NET Core File
   ↓
HTTP Response
   ↓
Binary data
   ↓
Angular Blob
   ↓
Browser download

Full Simple Sample Program

We'll implement:

  • Upload a PDF/image from Angular to ASP.NET Core.
  • Save it in an Uploads folder.
  • Download the saved file from Angular.
  • Use FormData for upload.
  • Use IFormFile in ASP.NET Core.
  • Use Blob for download.

ASP.NET Core — Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddCors(options =>
{
    options.AddPolicy("AngularApp", policy =>
    {
        policy
            .WithOrigins("http://localhost:4200")
            .AllowAnyHeader()
            .AllowAnyMethod();
    });
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseCors("AngularApp");

app.MapControllers();

app.Run();

ASP.NET Core — FilesController.cs

using Microsoft.AspNetCore.Mvc;

namespace FileApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class FilesController : ControllerBase
{
    private readonly IWebHostEnvironment _environment;

    public FilesController(IWebHostEnvironment environment)
    {
        _environment = environment;
    }

    // Upload
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(
        [FromForm] IFormFile file)
    {
        if (file == null || file.Length == 0)
        {
            return BadRequest("No file selected.");
        }

        var uploadFolder = Path.Combine(
            _environment.ContentRootPath,
            "Uploads");

        if (!Directory.Exists(uploadFolder))
        {
            Directory.CreateDirectory(uploadFolder);
        }

        var fileName = Path.GetFileName(file.FileName);

        var filePath = Path.Combine(
            uploadFolder,
            fileName);

        await using var stream =
            new FileStream(filePath, FileMode.Create);

        await file.CopyToAsync(stream);

        return Ok(new
        {
            message = "File uploaded successfully.",
            fileName
        });
    }


    // Download
    [HttpGet("download/{fileName}")]
    public IActionResult Download(string fileName)
    {
        fileName = Path.GetFileName(fileName);

        var filePath = Path.Combine(
            _environment.ContentRootPath,
            "Uploads",
            fileName);

        if (!System.IO.File.Exists(filePath))
        {
            return NotFound();
        }

        var bytes =
            System.IO.File.ReadAllBytes(filePath);

        return File(
            bytes,
            "application/octet-stream",
            fileName);
    }
}

Angular

First configure HttpClient.

app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient()
  ]
};

file.service.ts

import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class FileService {

  private http = inject(HttpClient);

  private apiUrl =
    'https://localhost:7001/api/files';


  uploadFile(file: File): Observable<any> {

    const formData = new FormData();

    formData.append(
      'file',
      file
    );

    return this.http.post(
      `${this.apiUrl}/upload`,
      formData
    );
  }


  downloadFile(fileName: string): Observable<Blob> {

    return this.http.get(
      `${this.apiUrl}/download/${encodeURIComponent(fileName)}`,
      {
        responseType: 'blob'
      }
    );
  }
}

file-upload.ts

import { Component, inject } from '@angular/core';

import { FileService } from './file.service';

@Component({
  selector: 'app-file-upload',
  standalone: true,
  templateUrl: './file-upload.html'
})
export class FileUpload {

  private fileService = inject(FileService);

  selectedFile: File | null = null;


  onFileSelected(event: Event): void {

    const input =
      event.target as HTMLInputElement;

    if (input.files &&
        input.files.length > 0) {

      this.selectedFile =
        input.files[0];
    }
  }


  upload(): void {

    if (!this.selectedFile) {
      return;
    }

    this.fileService
      .uploadFile(this.selectedFile)
      .subscribe({
        next: (response) => {
          console.log(
            'Upload successful',
            response
          );
        },
        error: (error) => {
          console.error(
            'Upload failed',
            error
          );
        }
      });
  }


  download(fileName: string): void {

    this.fileService
      .downloadFile(fileName)
      .subscribe({
        next: (blob) => {

          const url =
            window.URL.createObjectURL(blob);

          const anchor =
            document.createElement('a');

          anchor.href = url;
          anchor.download = fileName;

          anchor.click();

          window.URL.revokeObjectURL(url);
        },
        error: (error) => {
          console.error(
            'Download failed',
            error
          );
        }
      });
  }
}

file-upload.html

<h2>File Upload</h2>

<input
  type="file"
  (change)="onFileSelected($event)"
>

<button
  type="button"
  (click)="upload()"
  [disabled]="!selectedFile">
  Upload
</button>

<hr>

<h2>File Download</h2>

<button
  type="button"
  (click)="download('sample.pdf')">
  Download sample.pdf
</button>

1. How Do You Upload Files from Angular to ASP.NET Core?

The main technologies are:

Angular
   ↓
File
   ↓
FormData
   ↓
HttpClient.post()
   ↓
multipart/form-data
   ↓
ASP.NET Core
   ↓
IFormFile

Step 1 — Select the File

HTML:

<input
  type="file"
  (change)="onFileSelected($event)"
>

When the user selects a file:

onFileSelected(event: Event): void {

  const input =
    event.target as HTMLInputElement;

  if (input.files &&
      input.files.length > 0) {

    this.selectedFile =
      input.files[0];
  }
}

The browser provides the selected file as a JavaScript:

File

object.


Step 2 — Create FormData

Files aren't normally sent as a JSON DTO.

Instead, use:

FormData

Example:

const formData = new FormData();

formData.append(
  'file',
  file
);

Think of it as:

FormData
   │
   └── file
         ↓
      sample.pdf

Step 3 — POST the FormData

return this.http.post(
  `${this.apiUrl}/upload`,
  formData
);

The browser sends a request similar to:

POST /api/files/upload

Content-Type:
multipart/form-data; boundary=...

The actual request body contains the binary file data.


What is multipart/form-data?

multipart/form-data is an HTTP content type commonly used when sending:

  • Files
  • Form fields
  • Files + form data together

For example:

multipart/form-data

Part 1
Name: file
Value: sample.pdf binary data

Part 2
Name: description
Value: Product Manual

Important: Don't Manually Set Content-Type

When using FormData, don't normally do this:

const headers = {
  'Content-Type': 'multipart/form-data'
};

Instead:

this.http.post(
  url,
  formData
);

The browser automatically sets something like:

Content-Type:
multipart/form-data; boundary=----WebKitFormBoundary...

The boundary is important for separating individual form parts.


Step 4 — ASP.NET Core Receives IFormFile

Angular sends:

formData.append(
  'file',
  file
);

ASP.NET Core receives:

[FromForm] IFormFile file

Example:

[HttpPost("upload")]
public async Task<IActionResult> Upload(
    [FromForm] IFormFile file)
{
    // ...
}

The names should correspond:

Angular

formData.append("file", file)
                 ↑

ASP.NET Core

IFormFile file
          ↑

What is IFormFile?

IFormFile represents a file received in an HTTP request.

It provides information such as:

file.FileName
file.Length
file.ContentType

and methods such as:

file.CopyToAsync(stream)

Saving the File

First create a path:

var uploadFolder = Path.Combine(
    _environment.ContentRootPath,
    "Uploads");

For example:

ProductApi/
│
├── Controllers/
├── Program.cs
│
└── Uploads/
      └── sample.pdf

Then create a file stream:

await using var stream =
    new FileStream(
        filePath,
        FileMode.Create);

Copy the uploaded file:

await file.CopyToAsync(stream);

Upload Flow

User selects sample.pdf
        ↓
<input type="file">
        ↓
JavaScript File
        ↓
FormData
        ↓
formData.append("file", file)
        ↓
HttpClient.post()
        ↓
multipart/form-data
        ↓
ASP.NET Core
        ↓
[FromForm] IFormFile
        ↓
CopyToAsync()
        ↓
Uploads/sample.pdf

Upload File + Additional Data

A common real-world requirement is:

Upload an article image together with title/category information.

Angular can put both into FormData:

const formData = new FormData();

formData.append(
  'file',
  file
);

formData.append(
  'title',
  'Angular HttpClient'
);

formData.append(
  'categoryId',
  '10'
);

ASP.NET Core can receive a DTO:

public class UploadDto
{
    public string Title { get; set; }
        = string.Empty;

    public int CategoryId { get; set; }

    public IFormFile? File { get; set; }
}

Controller:

[HttpPost("upload")]
public IActionResult Upload(
    [FromForm] UploadDto request)
{
    var title = request.Title;

    var categoryId = request.CategoryId;

    var file = request.File;

    return Ok();
}

This is very useful when Angular sends a file plus ordinary form values.


Upload Multiple Files

Angular:

uploadFiles(files: File[]): Observable<any> {

  const formData = new FormData();

  for (const file of files) {

    formData.append(
      'files',
      file
    );
  }

  return this.http.post(
    `${this.apiUrl}/upload-multiple`,
    formData
  );
}

ASP.NET Core:

[HttpPost("upload-multiple")]
public IActionResult UploadMultiple(
    [FromForm] List<IFormFile> files)
{
    foreach (var file in files)
    {
        Console.WriteLine(file.FileName);
    }

    return Ok();
}

Mapping:

Angular

files
files
files
  ↓

ASP.NET Core

List<IFormFile>

2. How Do You Download Files from ASP.NET Core?

Download is essentially the reverse process:

ASP.NET Core
      ↓
File
      ↓
HTTP Response
      ↓
Angular HttpClient
      ↓
Blob
      ↓
Browser
      ↓
Save File

ASP.NET Core Download Endpoint

[HttpGet("download/{fileName}")]
public IActionResult Download(string fileName)
{
    fileName = Path.GetFileName(fileName);

    var filePath = Path.Combine(
        _environment.ContentRootPath,
        "Uploads",
        fileName);

    if (!System.IO.File.Exists(filePath))
    {
        return NotFound();
    }

    var bytes =
        System.IO.File.ReadAllBytes(filePath);

    return File(
        bytes,
        "application/octet-stream",
        fileName);
}

The important part is:

return File(
    bytes,
    "application/octet-stream",
    fileName);

ASP.NET Core sends the file as the HTTP response.


What is application/octet-stream?

It is a generic MIME type representing binary data.

application/octet-stream

It can be used when you want the browser to treat the response as a downloadable binary file.

When you know the actual MIME type, you can use a more specific type.

For PDF:

return File(
    bytes,
    "application/pdf",
    fileName);

For PNG:

return File(
    bytes,
    "image/png",
    fileName);

For Excel:

application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

Angular Download Service

The critical Angular code is:

downloadFile(
  fileName: string
): Observable<Blob> {

  return this.http.get(
    `${this.apiUrl}/download/${encodeURIComponent(fileName)}`,
    {
      responseType: 'blob'
    }
  );
}

The important setting is:

responseType: 'blob'

What is a Blob?

Blob means Binary Large Object in the browser API context.

It represents raw binary-like data such as:

PDF
Image
Excel
Word
ZIP
Other files

Normally, when Angular calls an API, we expect JSON:

this.http.get<Product[]>(url);

For a file, we're not expecting JSON.

We're expecting binary data:

this.http.get(
  url,
  {
    responseType: 'blob'
  }
);

Triggering the Browser Download

After receiving the Blob:

const url =
  window.URL.createObjectURL(blob);

This creates a temporary browser URL for the binary data.

Then:

const anchor =
  document.createElement('a');

Set:

anchor.href = url;
anchor.download = fileName;

Then:

anchor.click();

Finally:

window.URL.revokeObjectURL(url);

This releases the temporary object URL.


Complete Download Flow

User clicks Download
        ↓
Angular
        ↓
downloadFile("sample.pdf")
        ↓
HttpClient.get()
        ↓
responseType: "blob"
        ↓
GET /api/files/download/sample.pdf
        ↓
ASP.NET Core
        ↓
Find file
        ↓
File(...)
        ↓
Binary HTTP response
        ↓
Angular Blob
        ↓
createObjectURL()
        ↓
<a download>
        ↓
anchor.click()
        ↓
Browser downloads sample.pdf

JSON vs File Upload vs File Download

This distinction is important.

Normal JSON POST

this.http.post(
  url,
  product
);

Request:

Content-Type: application/json

ASP.NET Core:

[FromBody] CreateProductDto product

File Upload

this.http.post(
  url,
  formData
);

Request:

Content-Type: multipart/form-data

ASP.NET Core:

[FromForm] IFormFile file

File Download

Angular:

this.http.get(
  url,
  {
    responseType: 'blob'
  }
);

ASP.NET Core:

return File(...);

So remember:

Scenario Angular ASP.NET Core
Send JSON Object [FromBody] DTO
Upload file FormData [FromForm] IFormFile
Download file responseType: 'blob' File(...)

Important Security Points for File Uploads

In a real application, don't simply trust:

file.FileName
file.ContentType

At minimum, validate:

  • File size
  • Allowed file extensions/types
  • Generated server-side filename
  • Storage location
  • Authorization
  • Malware scanning where appropriate

Instead of storing an uploaded file directly using the user-provided filename, a safer pattern is to generate your own filename:

var extension =
    Path.GetExtension(file.FileName);

var storedFileName =
    $"{Guid.NewGuid()}{extension}";

Then save:

3a1bd71c-8d54-4e92-a6a7-....pdf

rather than blindly trusting:

user-supplied-file-name.pdf

Also ensure download paths cannot escape the intended storage directory; Path.GetFileName() in the simple example helps strip directory components, but production systems should apply a deliberate storage/access design.


Key Points

  • Use FormData to upload files from Angular.
  • Use HttpClient.post() to send the FormData.
  • File uploads normally use:
multipart/form-data
  • Don't manually set the multipart Content-Type header; allow the browser to generate the boundary.
  • ASP.NET Core receives uploaded files using:
IFormFile
  • Use:
[FromForm]

for multipart/form-data binding.

  • Multiple files can be received as:
List<IFormFile>
  • ASP.NET Core can return files using:
return File(...);
  • Angular should use:
responseType: 'blob'

when downloading binary files.

  • Use URL.createObjectURL() to create a temporary browser URL for a received Blob.
  • Validate file size, type, filename, authorization and storage location on the server.

Interview Questions and Answers

1. How do you upload a file from Angular to ASP.NET Core?

Create a FormData, append the file and send it using HttpClient.post():

const formData = new FormData();

formData.append('file', file);

return this.http.post(
  '/api/files/upload',
  formData
);

ASP.NET Core receives it using:

[FromForm] IFormFile file

2. Why do we use FormData for file uploads?

FormData allows the browser to construct a multipart/form-data request containing binary files and ordinary form fields.


3. What is IFormFile?

IFormFile is ASP.NET Core's abstraction representing a file uploaded as part of an HTTP request.

IFormFile file

4. Should you manually set Content-Type: multipart/form-data in Angular?

Normally, no.

When sending FormData, allow the browser to set the Content-Type together with the required multipart boundary.


5. How do you upload multiple files?

Angular can append multiple files:

for (const file of files) {
  formData.append('files', file);
}

ASP.NET Core can receive:

[FromForm] List<IFormFile> files

6. How does ASP.NET Core return a file?

Using the controller File() method:

return File(
    bytes,
    "application/pdf",
    "sample.pdf"
);

7. How does Angular download a file?

Request it as a Blob:

return this.http.get(
  url,
  {
    responseType: 'blob'
  }
);

Then create an object URL and trigger the browser download.


8. Why do we use responseType: 'blob'?

Because the response contains binary file data rather than the normal JSON response.


9. What is the difference between [FromBody] and [FromForm]?

[FromBody]
    ↓
Typically JSON request body
    ↓
DTO


[FromForm]
    ↓
Form / multipart request
    ↓
Fields + IFormFile

For JSON:

[FromBody] CreateProductDto product

For file upload:

[FromForm] IFormFile file

Short Interview Answer

To upload files from Angular, create a FormData, append the selected File, and send it using HttpClient.post(). ASP.NET Core receives the multipart request using [FromForm] IFormFile. For downloads, ASP.NET Core returns the file using File(), while Angular requests it with responseType: 'blob' and uses the resulting Blob to trigger the browser download.

← Back to Articles