November 22, 2019

Upload Files and Images in Angular

Hello everyone, in this tutorial we will create a simple app which will have the functionality to upload files and images in Angular . We will be using Asp.Net Web API as our back-end,and will save the uploaded files on a local folder along with a reference in the database.

Upload-Files-and-Images-in-Angular

We will be using Bootstrap to design our form, table and the html input tag with the type=”file” attribute to upload the files. Let us now start first with the back-end API implementation.

Implement the backend with Asp.Net Web API

Project Setup

Create a empty Asp.Net Web API project and add two a controller named UploadController and three model classes for Images, Files/Resume and the user records each.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace FileUpload_API.Models
{
    public class Resume
    {
        public int Id { get; set; }

        public int UserId { get; set; }

        public string ResumeFileName { get; set; }

        public string StorageName { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace FileUpload_API.Models
{
    public class ProfileImage
    {
        [Key]
        public int Id { get; set; }

        public int UserId { get; set; }

        public string ImageFileName { get; set; }

        public string StorageName { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace FileUpload_API.Models
{
    public class UserDetail
    {
        [Key]
        public int Id { get; set; }
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public long Phone { get; set; }

        public string Email { get; set; }

        [NotMapped]
        public string Image { get; set; }

        [NotMapped]
        public string Resume { get; set; }

        [NotMapped]
        public string ResumeStorage { get; set; }
    }
}

Add Entity Framework to Project

We will now add new Data Upload-EDMX project to our solution and select the Code First approach to use our models to build the database. Add the code below to generate the tables

namespace FileUpload_API
{
    using System;
    using System.Data.Entity;
    using FileUpload_API.Models;
    using System.Linq;

    public class Upload_EDMX : DbContext
    {
        // Your context has been configured to use a 'Upload_EDMX' connection string from your application's 
        // configuration file (App.config or Web.config). By default, this connection string targets the 
        // 'FileUpload_API.Upload_EDMX' database on your LocalDb instance. 
        // 
        // If you wish to target a different database and/or database provider, modify the 'Upload_EDMX' 
        // connection string in the application configuration file.
        public Upload_EDMX()
            : base("name=Upload-EDMX")
        {
        }

        // Add a DbSet for each entity type that you want to include in your model. For more information 
        // on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109.

        // public virtual DbSet<MyEntity> MyEntities { get; set; }

        public virtual DbSet<UserDetail> UserDetails { get; set; }
        public virtual DbSet<ProfileImage> ProfileImages { get; set; }

        public virtual DbSet<Resume> Resumes { get; set; }
    }

    //public class MyEntity
    //{
    //    public int Id { get; set; }
    //    public string Name { get; set; }
    //}
}

Now use the Packet Console Manager to enable migrations and update the database.

  • Enable-Migrations
  • Add-Migration <SomeComment>
  • update-database

We will not be updating the connection string so as to use the built in SQL Server of the Visual Studio.

Add the Controller Code

using FileUpload_API.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;

namespace FileUpload_API.Controllers
{
    [EnableCors("*", "*", "*")]
    [RoutePrefix("Upload")]
    public class UploadController : ApiController
    {
        Upload_EDMX context = new Upload_EDMX();
        [HttpPost]

        public void CreateUserWithAttachment()
        {

            var httpRequest = HttpContext.Current.Request;
            var files = httpRequest.Files;
            var userDetail = JsonConvert.DeserializeObject<UserDetail>(httpRequest.Form["Details"]);
            var image = files[0] != null ? files[0] : null;
            var resume = files[1] != null ? files[1] : null;

            context.UserDetails.Add(userDetail);
            context.SaveChanges();

            if(image != null)
            {
                UploadImage(image,userDetail.Id);
            }

            if(resume != null)
            {
                UploadResume(resume, userDetail.Id);
            }
        }

        [HttpGet]
        public IEnumerable<UserDetail> GetDetails()
        {
            var details =  (from user in context.UserDetails
                           join profile in context.ProfileImages
                           on user.Id equals profile.UserId
                           join resume in context.Resumes
                           on user.Id equals resume.UserId
                           select new
                           {
                               FirstName = user.FirstName,
                               LastName = user.LastName,
                               Email = user.Email,
                               Phone = user.Phone,
                               Image = profile.StorageName,
                               Resume = resume.ResumeFileName,
                               ResumeStorage = resume.StorageName

                           }

                           ).ToList().Select(x=> new UserDetail()
                           {
                               FirstName = x.FirstName,
                               LastName = x.LastName,
                               Email = x.Email,
                               Phone = x.Phone,
                               Image = x.Image,
                               Resume = x.Resume,
                               ResumeStorage = x.ResumeStorage

                           })
                           ;

            return details;
        }

        private void UploadResume(HttpPostedFile resume, int id)
        {
            string resumeName  = GenerateRandomFileName(resume);
            var PathToSave = HttpContext.Current.Server.MapPath("/Attachments/" + resumeName);

            resume.SaveAs(PathToSave);

            Resume obj = new Resume();
            obj.ResumeFileName = resume.FileName;
            obj.StorageName = resumeName;
            obj.UserId = id;

            context.Resumes.Add(obj);
            context.SaveChanges();
        }

        private static string GenerateRandomFileName(HttpPostedFile file)
        {
            Guid guid = Guid.NewGuid();

            string fileName = Path.GetFileNameWithoutExtension(file.FileName);
            fileName = fileName + "_" + guid + Path.GetExtension(file.FileName);

            return fileName;
        }

        private void UploadImage(HttpPostedFile image, int id)
        {
            string imageName = GenerateRandomFileName(image);
            var PathToSave = HttpContext.Current.Server.MapPath("/Attachments/" + imageName);

            image.SaveAs(PathToSave);

            ProfileImage obj = new ProfileImage();
            obj.ImageFileName = image.FileName;
            obj.StorageName = imageName;
            obj.UserId = id;

            context.ProfileImages.Add(obj);
            context.SaveChanges();
        }
    }
}

Implement HttpPost Logic

  • We will use this end-point to create a new records in the system.
  • The HttpContext object will contain the two uploaded files and the UserDetail model with the required details.
  • Extract the image and resume from the context object.
  • Add the UserDetail record to the DB using Entity Framework
  • Call the UploadResume and UploadImage functions to upload the required files
  • We will append a GUID at the end of all files which are uploaded so as the resolve the conflict in case multiple files of same names are uploaded.
  • Save the records in concerned tables and same the files in the Attachments folder inside the project directory (HttpContext.Current.Server.MapPath) using the save method.
  • To refer the files for future reference, we will use the FileName stored in the table and get it from the save location.

Implement HttpGet Logic

In the UserDetail object we have created some extra field which were not mapped to the DB so that they can be used for saving the filenames and storage location.

Also Read: Transform Http Requests in Angular using Interceptor

Use LINQ to fetch the required records and generate an IEnumerable<UserDetail> type of object to shared with the client.

Enable CORS

Use the below command to install the packages for Cors which will allow our Web API end points to be accessed from cross origin URL’s.

Install-Package Microsoft.AspNet.WebApi.Cors -pre -project WebService

  • Add config.EnableCors() in the WebApiConfig file
  • Decorate the UploadController with [EnableCors(““, ““, “*”)] attribute to allow access from all sources.

Implement Client side app with Angular

Use Angular CLI to add the following items into your Angular project:

  • Upload Component
  • View Component
  • Upload Service
  • User Detail Model Class

Create the UserDetailModel class

export class UserDetailModel {
    
  FirstName:string;
  LastName:string;
  Phone:Number;
  Email:string;
  Image:string;
  Resume:string;
  ResumeStorage:string;    
}

Create the UploadService

import { Injectable } from '@angular/core';
import { UserDetailModel } from './user-detail-model';
import { HttpClient, HttpClientModule, HttpParams } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class UploadServiceService {

  user: UserDetailModel = new UserDetailModel;
  userList: UserDetailModel[] = [];
  api_url = "http://localhost:52862/api/";
  attachment_BaseLocations = "http://localhost:52862/Attachments/";


  constructor(private httpVariable: HttpClient) { }

  UploadAttachements(formData) {
    this.httpVariable.post(this.api_url + "Upload", formData)
      .subscribe
      ((response) => {
        console.log("uploaded");
        this.GetDetails();
        console.log(this.userList);},
        
   
      );
  }

  GetDetails() {
    this.httpVariable.get(this.api_url + "Upload")
    
         .subscribe((respones) => {
        console.log(respones);
        this.userList = respones as UserDetailModel[];
      })
  }



}
  • Add the required imports for HttpClientModule and UserDetailModel
  • Create a variable of UserDetailModel array which will store our records
  • GetDetails method calls the HttpGet API end-point to fetch all the stored records in the database
  • UploadAttachment method calls the HttpPost end-point and also calls the GetDetails method to fetch the newly added record as well.

upload.component.ts

import { Component, OnInit, Input, ElementRef, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators, Form } from '@angular/forms';
import { UploadServiceService } from '../upload-service.service';
import { UserDetailModel } from '../user-detail-model';


@Component({
  selector: 'app-upload',
  templateUrl: './upload.component.html',
  styleUrls: ['./upload.component.css']
})
export class UploadComponent implements OnInit {

  fileDetails: File;
  imageDetails: File;
  @ViewChild('image',{static:true}) imageControl : ElementRef;
  @ViewChild('resume',{static:true}) resumeControl : ElementRef;


  constructor(
    private uploadService: UploadServiceService
  ) { }

  ngOnInit() {
    this.uploadService.GetDetails();
  }

  resumeUpload(event) {
    this.fileDetails = event.target.files[0];
  }

  imageUpload(event) {
    this.imageDetails = event.target.files[0];
  }

  onSubmit() {
    const formData: FormData = new FormData();
    formData.append("Image", this.imageDetails, this.imageDetails.name);
    formData.append("Resume", this.fileDetails, this.fileDetails.name);
    formData.append("Details", JSON.stringify(this.uploadService.user));
    this.uploadService.UploadAttachements(formData);
    this.ClearForm();
    
  }

  ClearForm()
  {
    this.uploadService.user = new UserDetailModel;
    this.imageControl.nativeElement.value= "";
    this.resumeControl.nativeElement.value= "";
  }


}
  • Add the required imports for HttpClientModule, UploadService and Form
  • We have used the @ViewChild to refer to our File inputs as they cannot be accessed via the Angular Forms directives
  • ResumeUpload and ImageUpload function handle the change event of respective file upload inputs using its inbuilt change method. The change method is fired everytime a new file is added to the input.
  • In the onSubmit method we create a new FormData object as we will have to implicitly add the Files to the existing form values.

upload.component.html

<h3 class="mt-3">
    File Upload with Angular</h3>
<form #form="ngForm" autocomplete="off" (submit)="onSubmit(form)">
    <div class="form-group">
        <input type="text" placeholder="FirstName" class="form-control" name="FirstName" #firstname [(ngModel)]="uploadService.user.FirstName" required>
    </div>
    <div class="form-group">
        <input type="text" placeholder="LastName" class="form-control" name="LastName" #lastname [(ngModel)]="uploadService.user.LastName" required>
    </div>
    <div class="form-group">
        <input type="text" placeholder="Phone Number" class="form-control" name="Phone" #phone [(ngModel)]="uploadService.user.Phone" required>
    </div>
    <div class="form-group">
        <input type="text" placeholder="Email Address" class="form-control" name="Email" #email [(ngModel)]="uploadService.user.Email">
    </div>

    <div class="form-group">
        <span class="label label-primary">Profile Image</span>
        <input type="file" (change)="imageUpload($event)" class="form-control" accept="image/*" #image>
    </div>

    <div class="form-group">
        <span class="label label-primary">Resume</span>
        <input type="file" (change)="resumeUpload($event)" class="form-control" #resume>
    </div>


    <button type="submit" class="btn btn-primary">Submit</button>

</form>

view.component.ts

import { Component, OnInit } from '@angular/core';
import { UploadServiceService } from '../upload-service.service';

@Component({
  selector: 'app-view',
  templateUrl: './view.component.html',
  styleUrls: ['./view.component.css']
})
export class ViewComponent implements OnInit {

  constructor( private uploadService: UploadServiceService) { }

  ngOnInit() {
  }

}

We just have to inject our UploadService into the component.

view.component.html

<h3 class="mt-3">Uploaded Records</h3>

<table class="table table-bordered">
    <thead class="thead-light">
        <th>Image</th>
        <th>Name</th>
        <th>Phone</th>
        <th>Email</th>
        <th>Resume</th>
    </thead>
    <tbody>
        <tr *ngIf="uploadService.userList.length ==0">
            <td class="font-italic text-center" colspan="5">
                No records found..
            </td>
        </tr>
        <tr *ngFor="let item of uploadService.userList ; let i = index;">
            <td><img [src]='uploadService.attachment_BaseLocations + item.Image' width="100" height="100" alt=""></td>
            <td>{{item.FirstName + " " + item.LastName}}</td>
            <td>{{item.Phone}}</td>
            <td>{{item.Email}}</td>
            <td>
                <a target="_blank" [href]='uploadService.attachment_BaseLocations + item.ResumeStorage'>{{item.Resume}}</a> </td>
        </tr>

    </tbody>
</table>
  • Use parameter binding in angular pass the src and href attributes of the <img> and <a> elements
  • We generate these values by combining the variable having the storage path on server, with the file name returned by the Api.

Note: Use the storage name in both the cases as the files are saved with modified names.

app.component.html

<div class="container-fluid">
    <div class="row">
        <div class="col-md-4">
            <app-upload></app-upload>
        </div>
        <div class="col-md-8">
            <app-view></app-view>
        </div>
    </div>
</div>

No code changes are required in the app.component.ts file

That’s it!! Our app is now ready to save images and files and fetch them to show on screen as well.

Make sure to drop a comment if you liked this post or in case you have any feedback which could help me in writing better tutorials for people like us who want to learn practically.

One thought on “Upload Files and Images in Angular

Leave a Reply

Your email address will not be published. Required fields are marked *