ASP.NET Core. Recipe #1. File(-s) upload/download with Postman and Angular 5+
The purpose of this series is to have a minimalistic example of a valid and working solution. No BS stuff which only distracts from the main theme. I also assume that you can scaffold the projects on the given technologies using any available tool (IDE, command prompt, etc). I’ll list all technologies that’ll be used in any given recipe but won’t go deep trying to setup them all. Each of them has excellent guides so I urge you to use them.
Also, I decided to use Medium code blocks and not a third party solution, because I want the recipe to be available as fast as possible, in the native Medium format.
Enjoy!
Prerequisites
Upload
Backend
- Configure
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// CORS for default Angular app
app.UseCors(builder =>
builder.WithOrigins("http://localhost:4200", "https://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
);
}
- Configure controller
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
// IMPORTANT: 'key' must match 'parameter name' in controller action /*
Postman:
Url: <host>/api/values/uploadfile
Verb: POST
Body: form-data
key (file): file
value: selected file
Headers: 'multipart/form-data' is set implicitly, no need to add manually
*/
[HttpPost("UploadFile")]
public IActionResult UploadFile(IFormFile file)
=> Ok(file.Length); /*
Postman:
Url: <host>/api/values/uploadfiles
Verb: POST
Body: form-data
key (file): files
value: selected files
Headers: 'multipart/form-data' is set implicitly, no need to add manually
*/
[HttpPost("UploadFiles")]
public IActionResult UploadFiles(List<IFormFile> files) // or IFormFileCollection
=> Ok(files.Sum(file => file.Length));
}
Frontend
- Component
*.html
<input type="file" multiple placeholder="Upload file(-s)" (change)="onFilesSelected($event)">
- Component
*.ts
import {Component, OnInit} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {DomSanitizer} from '@angular/platform-browser';export class AppComponent implements OnInit {
constructor(private httpClient: HttpClient) onFilesSelected(evt: Event) {
const files: FileList = (evt.target as HTMLInputElement).files; const formData = new FormData();
for (let i = 0; i < files.length; i++) {
const file = files[i];
// IMPORTANT: 'files' must match parameter name in controller's action
formData.append('files', file, file.name);
}
const headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data'); this.httpClient
.post('<host>/api/values/UploadFiles', formData, { headers })
.subscribe(result => console.log(`uploaded: ${result}`));
}
}
Download
Backend
- Create any
*.jpg
file in the root folder (Ex.image.jpg
) - Configure
Startup.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;public class Startup
{
private readonly IHostingEnvironment _env; public Startup(IHostingEnvironment env)
=> _env = env; public void ConfigureServices(IServiceCollection services)
{ /*
By default, we already have 'IFileProvider' set -PhysicalFileProvider.
Just use DI to access it in a controller
*/
services.AddSingleton<IFileProvider>(_env.ContentRootFileProvider);
}
}
- Configure controller
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileProviders;[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IFileProvider _fileProvider; public ValuesController(IFileProvider fileProvider)
=> _fileProvider = fileProvider; [HttpGet("{imageName}")]
public IActionResult GetImageFile(string imageName)
{
var name = $"{imageName}.jpg";
var fileInfo = _fileProvider.GetFileInfo(name);
var data = System.IO.File.ReadAllBytes(fileInfo.PhysicalPath);
return File(data, MediaTypeNames.Image.Jpeg, name);
}
}
Frontend
- Component
*.html
<h1>Image</h1>
<img [src]="imageUrl$ | async">
- Component
*.ts
import {Component, OnInit} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
import {Subscriber} from 'rxjs/Subscriber';
import {switchMap} from 'rxjs/operators';export class AppComponent implements OnInit {
constructor(private httpClient: HttpClient, private sanitizer: DomSanitizer) { }public imageUrl$: Observable<any>;public ngOnInit(): void {
this.imageUrl$ = this.fetchImage('<host>/api/values/image');
}private fetchImage(url: string): Observable<any> {
return this.httpClient
.get(url, { responseType: 'blob' })
.pipe(switchMap(blob => {
return Observable.create((observer: Subscriber<SafeUrl>) => {
const objectUrl = URL.createObjectURL(blob);
const safe = this.sanitizer.bypassSecurityTrustUrl(objectUrl);
observer.next(safe);
return () => {
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
}
};
});
}));
}
}