惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
量子位
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
IT之家
IT之家
F
Fortinet All Blogs
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Register - Security
The Register - Security
NISL@THU
NISL@THU
Webroot Blog
Webroot Blog
A
Arctic Wolf
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
Recent Announcements
Recent Announcements
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog
P
Palo Alto Networks Blog
Y
Y Combinator Blog
WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
AWS News Blog
AWS News Blog
有赞技术团队
有赞技术团队
Engineering at Meta
Engineering at Meta
C
Cybersecurity and Infrastructure Security Agency CISA
aimingoo的专栏
aimingoo的专栏
Know Your Adversary
Know Your Adversary
Cyberwarzone
Cyberwarzone
Martin Fowler
Martin Fowler
The Hacker News
The Hacker News
P
Privacy International News Feed
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
宝玉的分享
宝玉的分享
博客园 - 聂微东
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
S
Securelist
T
The Exploit Database - CXSecurity.com
T
Threatpost
Microsoft Azure Blog
Microsoft Azure Blog
The Cloudflare Blog
F
Full Disclosure

ImageKit.io Blog

Next.js Image Optimization with ImageKit Use Video as a Background in Your Next.js Project How to Fix Autoplay Video in Next.js How Durian Scaled a Visual-First Retail Experience to 350K Monthly Visitors Online How Matsmart accelerated image delivery across countries with ImageKit AI in Digital Asset Management: From Smart Workflows to Agentic Automation How Joseph Joseph unified and secured global video delivery with ImageKit How Modall powers fast, effortless media delivery across 40+ projects with ImageKit Digital Asset Management (DAM) Trends: 2026 Report How to add a poster image to Video.js player (and automate it) HLS streaming with Video.js + React Building the future of storytelling with fast, AI-powered video delivery How PushOwl delivers 100M+ image-rich notifications seamlessly with ImageKit How Homify delivers millions of interior design images seamlessly with ImageKit Better event discovery with lightning‑fast videos & images Adding video player in React Native Video player in Angular applications Crop and resize videos in React Next.js image and video upload React image and video upload React video optimization How we quadrupled our traffic to 625K monthly page views How Apollo 24|7 boosted performance & reduced costs with ImageKit Simplify your media workflows with ImageKit DAM integrations Extending Lighthouse for custom image and video optimization analysis Brand Asset Management: What is it? How does it work? WordPress Digital Asset Management Guide - Manage your WP media assets better Why Shopify retailers need a digital asset management solution DAM vs. SharePoint: Which is best for you? AI-powered Metadata and Tagging in Digital Asset Management How Hopscotch built India's largest online Kids' fashion brand with ImageKit Dropbox Vs. DAM: Which Is The Right Tool For Digital Asset Management Digital Asset Management for Photographers: A Complete Guide Why digital asset management for agencies is essential Helping both Top and Bottom Line: SaffronStays rapid, profitable growth with ImageKit How KreditBee simplified media experiences with ImageKit Google Drive alternatives for businesses (with fast-growing teams) Node.js image upload ImageKit: The Secret Ingredient in Swiggy’s Expansion Journey Streamlining the Design Approval Process: A Comprehensive Guide AV1 Codec - Complete guide for video application devs PHP image and video upload AV1 vs VP9: Which codec should you choose? Adding video player in Next.js React Video Player VP8 vs VP9 - In the context of online video delivery Exploring WebM vs MP4 7 Free Digital Asset Management Software that are not Open-Source Comparing 9 Top Digital Asset Management Tools in the Market What are Brand Standards and Why do they Matter? Boost Sales and Brand Appeal: Essential Tips for eCommerce Image Management Brand Recall: The Strategy to Create Unforgettable Brands How to upload files in HTML? Branding for Small Businesses (2025 Edition) Everything you need to know about VP9 codec Recent updates from ImageKit and what's next Best Ways to Write RFP For Digital Asset Management (+ with free RFP template) What is Brand Dilution? How to Avoid It? Explained with [Examples] The Importance of Brand Identity: Leveraging Digital Asset Management for Impact From Launch to Scale: How to Launch a Brand Campaign Digital Asset Management Requirements - What do You Need to Evaluate and How? Marketing Collateral Management: A Quick End-to-End Guide Video Content Management System: What Is It And How To Choose One? Dropbox vs. Google Drive vs. Onedrive: The Best Cloud Storage Solution How to Build Brand Trust: Get Started In 2025 Google Drive vs. Box: A Detailed Comparison How Digital Asset Management Solutions Help Protect Brand Equity A DAM Solution Can Safeguard Your Digital Intellectual Property - Here’s How WebP Vs. PNG: Which Image Format Should You Use and Why? How to Resize Images in Bootstrap Easily Progressive jpegs (PJPEG): the key to loading images faster on your website Dropbox vs. Google Drive: The Best Cloud Storage For Digital Assets Dropbox Pros & Cons In 2024: An In-Depth Analysis and Why A DAM Solution Stands Out Google Drive Vs OneDrive: The Better Storage Option For Digital Assets Manage your video assets better with video metadata Understanding DAM's Role in Strengthening Brand Identity Digital asset management strategy: What to know before creating one The Ultimate Guide To Marketing Agency Onboarding 6 Solutions To Simplify Large File Sharing Over The Web A Step-by-Step Breakdown of a Video Production Workflow 13 Digital Asset Management Use Cases You Should Know How to Conduct a Brand Audit and Manage Your Brand Assets Costly Consequences of Inconsistent Branding And How DAM Can Help Dynamic Asset Transformation: What It Is, Why You Need It, and How ImageKit Can Help Everything You Need to Know About HTML Video Autoplay How To Select Your DAM Vendor: A Complete Guide How to Boost User Experience with Smart Digital Asset Management React Image Optimization: A Guide for Web Developers Why Should DAM Be A Part Of Your MarTech Stack? Unleashing the Power of Content Repurposing with ImageKit MKV vs MP4: Which Video File Format Is Better for Your Needs? Digital Asset Management For Ecommerce: A Complete Guide How an Image Tagging Software can Transform Your Image Search How to Manage Your Content Lifecycle Effectively M4V vs MP4: Which Video Format Should You Use and Why? Why Every Business Needs An Image Management System All The Questions To Ask During A Dam Demo Which is the Best Image Format for Your Website? Uploading Multiple Files Using JavaScript: A Comprehensive Guide Headless DAM: Why API-Driven Digital Asset Management is the Way Forward
Angular image & video upload
Ankur Dwivedi · 2024-03-26 · via ImageKit.io Blog

In this blog post, you will learn step-by-step how to handle file uploads in an Angular application.

You can see the working app here and get the entire source code from https://github.com/imagekit-samples/tutorials/tree/angular-file-upload.

The final result will look like this:

This guide walks you through the following topics:

  • Setting up an angular application
  • Creating a customized file upload interface
  • Setting up the backend
  • Implementing a progress bar
  • Adding basic validation
  • What's wrong with this approach?
  • Uploading files to ImageKit directly from the browser

Setting up an angular application

In this tutorial, we'll be using:

  • Node version 20.
  • Angular version 17.

We'll start by creating a new project using Angular's ng CLI.

Here are the steps:

  1. Run the following command to create a new Angular project:
    ng new imagekit-angular-app --strict=false

    It will prompt a few questions. In this tutorial, we will make the following choices as shown below.

    • Which stylesheet format would you like to use? CSS
    • Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No
  2. Navigate to the newly created project directory:
    cd imagekit-angular-app/

  3. Install libraries (if not already):
    npm install

  4. Start the application by running:
    npm start

💡

If npm start gives you any issues, you can run our Angular app using the command
ng serve --port 4200 --host 127.0.0.1.

Open your web browser and go to http://localhost:4200/ to see a dummy app created by the Angular CLI.

Dummy angular app
Dummy angular app

Let's remove everything from src/app/app.component.html so we can start fresh. Begin by adding the basic <input/> element to pick files. Use this code in src/app/app.component.html:

<h1>Angular image & video upload</h1>
<input type="file" name="file" />
Basic upload button
Basic upload button

This upload button is very simple. It just opens a file picker dialog. We will instead use a custom file picker component and remove the default file picker input.

Custom file upload interface

Let's make a new component called upload-form using the ng generate command provided by the Angular CLI.

ng generate component upload-form

Now, let's make changes to our upload-form component. First, open the upload-form/upload-form.component.html file and add this code:

<h1>Angular image & video upload</h1>
<form class="upload-form">
  <h1>Upload File</h1>

  <label
    for="file"
    (dragover)="handleDragOver($event)"
    (drop)="handleDrop($event)"
  >
    <i class="ph ph-upload"></i>

    <span>
      Drag & drop or
      <span>browse</span>
      your files
    </span>
  </label>

  <input
    id="file"
    type="file"
    name="file"
    (change)="onFileSelected($event, null)"
  />

  <div class="result" [style.display]="outputBoxVisible ? 'flex' : 'none'">
    <i class="ph ph-file"></i>
    <div class="file-details">
      <span class="file-name">{​{ fileName }​}</span>
      <ng-container *ngIf="uploadStatus === 200 || uploadStatus === undefined">
        <span class="file-size">{​{ fileSize }​}</span>
      </ng-container>
    </div>

    <div class="upload-result" [style.display]="uploadStatus ? 'flex' : 'none'">
      <span>{​{ uploadResult }​}</span>
      <ng-container *ngIf="uploadStatus === 200; else error">
        <i class="ph ph-check-circle"></i>
      </ng-container>
      <ng-template #error>
        <i class="ph ph-x-circle"></i>
      </ng-template>
    </div>
  </div>
</form>

Next, we'll give our component some style by adding CSS to the upload-form/upload-form.component.css file. Add the CSS code from the provided file.

https://github.com/imagekit-samples/tutorials/blob/angular-file-upload/src/app/upload-form/upload-form.component.css

To integrate icons, insert the provided script tag into the index.html file. We're utilising phosphor-icons for this purpose.

<script src="https://unpkg.com/@phosphor-icons/web"></script>

Now, let's include our upload-form component in our src/app/app.component.ts file.

import { Component } from '@angular/core';
import { UploadFormComponent } from './upload-form/upload-form.component';
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [UploadFormComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'imagekit-angular-app';
}

Let's also update src/app/app.component.html.

<app-upload-form />

We've built our UI, but it won't function properly until we create the functions  onFileSelected and handleDrop, which we referenced earlier in our UI.

Let's update the upload-form/upload-form.component.ts  file.

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-upload-form',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './upload-form.component.html',
  styleUrl: './upload-form.component.css',
})
export class UploadFormComponent {
  outputBoxVisible = false;
  progress = `0%`;
  uploadResult = '';
  fileName = '';
  fileSize = '';
  uploadStatus: number | undefined;

  constructor() {}

  onFileSelected(event: any, inputFile: File | null) {
    this.outputBoxVisible = false;
    this.progress = `0%`;
    this.uploadResult = '';
    this.fileName = '';
    this.fileSize = '';
    this.uploadStatus = undefined;
    const file: File = inputFile || event.target.files[0];

    if (file) {
      this.fileName = file.name;
      this.fileSize = `${(file.size / 1024).toFixed(2)} KB`;
      this.outputBoxVisible = true;

      const formData = new FormData();
      formData.append('file', file);

      const xhr = new XMLHttpRequest();
      xhr.open('POST', 'http://localhost:4000/upload', true);

      xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
          if (xhr.status === 200) {
            this.uploadResult = 'Uploaded';
          } else if (xhr.status === 400) {
            this.uploadResult = JSON.parse(xhr.response)!.message;
          } else {
            this.uploadResult = 'File upload failed!';
          }
          this.uploadStatus = xhr.status;
        }
      };

      xhr.send(formData);
    }
  }

  handleDragOver(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
  }

  handleDrop(event: DragEvent) {
    event.preventDefault();
    if (event.dataTransfer) {
      const file: File = event.dataTransfer.files[0];
      this.onFileSelected(event, event.dataTransfer.files[0]);
    }
  }
}

In the upload-form-component.ts file:

  • We obtain files using a file picker through the change event or directly through the drop event when a file is dropped into the file input UI. In both cases, we utilize the onFileSelected method which accepts an event as input.
  • The onFileSelected method retrieves the file from the event based on its type. When triggered by the input change event, the file is obtained as HTMLInputElement.files, which is essentially a fileList. We select a single file by accessing event.target.files[0].
  • In the case of a drop event, the file is obtained from the dataTransfer property of the event, which contains a list of files. We access a single file using event.dataTransfer.files[0].
  • This method extracts the file name and size, appends the file as form data, and then initiates an XMLHttpRequest to /api/upload.
  • After sending the request, it updates the result based on the status received from the API.
  • handleDrop function is invoked when a file is dragged and dropped into the file input container, triggering a drop event. It then passes the event to the onFileSelected method.
  • handleDragOver function is invoked when the dragover event occurs. It's essential to execute preventDefault() and stopPropagation() during dragover to ensure the proper functionality of the drop operation.

Here's how our application will look now. If we attempt to upload a file, it'll fail because we still need to set up a backend service. Let's create a backend service to provide an endpoint at http://localhost:4000/upload.

Failed file upload
Failed file upload

Setting up the backend

Now we will create a Node.js application to accept a file and store it locally. Run the below commands to create a node application.

mkdir server
cd server
npm init -y
npm install express multer cors nodemon

Create a server.js file and add the code below to it.

const express = require("express");
const multer = require("multer");
const path = require("path");
const cors = require("cors");

const app = express();
const PORT = 4000;

app.use(cors());

app.use((req, _, next) => {
  console.log("Request received at:", req.path);
  next();
});

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/");
  },
  filename: function (req, file, cb) {
    cb(
      null,
      file.fieldname + "-" + Date.now() + path.extname(file.originalname)
    );
  },
});

const upload = multer({
  storage: storage
});

app.post("/upload", upload.single("file"), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ message: "No file uploaded" });
  }
  res.status(200).json({
    message: "File uploaded successfully",
    filename: req.file.filename,
  });
});

app.use("/uploads", express.static("uploads"));

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

In the server.js file, we're configuring a Node.js server with Express to manage file uploads, and it's set to listen on port 4000.

  • We are using the cors middleware to enable Cross-Origin Resource Sharing, which permits the server to specify origins from which browsers can safely load resources. This is necessary because our Angular application is running on http://localhost:4200, while our server is running on http://localhost:4000. Since they are not running on the same origin, the browser would block the requests without CORS headers.
  • We have defined a POST route ("/upload") where files can be uploaded. It uses the multer library to handle file uploads.
  • Multer is used as a middleware to manage multipart/form-data. We've configured it to save files in the uploads directory and to change the file name when uploading.

Let’s create a directory server/uploads to store files uploaded. Additionally, we need to update the server/package.json file. Refer to the code below:

{
  "name": "server",
  ...
  "scripts": {
    ...
    "start": "nodemon server.js"
  },
  ...
}

To run the backend service, run the following command inside the server directory.

npm start

Now, when we run our application and select a file, it'll be uploaded successfully. We can verify this by checking the uploads directory, where we'll find the uploaded file.

Successful file upload
Successful file upload

With the current setup, we have to execute two different commands to run our backend and frontend separately. We can simplify this by using the concurrently package.

npm i --save-dev concurrently

Now, let's update the scripts section in the package.json file.

"scripts": {
    "ng": "ng",
    "start": "concurrently \"npm run start:server\" \"npm run start:frontend\"",
    "start:server": "cd server && npm start",
    "start:frontend": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
},

Now, we can start both our Angular app and backend by running the command:

npm start

To simplify our API requests, we can configure a proxy.
Create a file named proxy.conf.json  in the src directory and add the following code to it:

{
  "/api/**": {
    "target": "http://localhost:4000",
    "secure": false,
    "changeOrigin": true,
    "pathRewrite": {
      "^/api": ""
    }
  }
}

Now, update the angular.json file. Inside the projects/imagekit-angular-app/architect/serve section, add the following code:

"options": {
    "proxyConfig": "src/proxy.conf.json"
},

Progress bar

Let's incorporate a progress bar into our component to track the file upload progress. Let's start with a progress bar UI.

Add this code to the upload-form/upload-form.component.html file.

<div class="file-details">
  <span class="file-name">{​{ fileName }​}</span>
  <ng-container *ngIf="uploadStatus === 200 || uploadStatus === undefined">
    <div class="progress-bar">
      <div class="progress" [style.width]="progress"></div>
    </div>
    <span class="file-size">{​{ fileSize }​}</span>
  </ng-container>
</div>

Next, let's capture the upload progress using the xhr.upload.onprogress function and display it in our UI. Add the following code to upload-form/upload-form.component.ts file at the end of the onFileSelected function.

xhr.upload.onprogress = (progressEvent) => {
    if (progressEvent.lengthComputable) {
      const progress = (progressEvent.loaded / progressEvent.total) * 100;
      this.progress = `${Math.round(progress)}%`;
    }
};

Now our custom upload component will look like this.

progress-bar
progress-bar

Adding basic validation

Let's implement some simple checks regarding file size and file type on the Node.js backend.

We'll update the server.js file to only accept images and videos with a size less than 2MB. We'll restrict the file size using the multer limits property and add a fileFilter function to validate files based on MIME type.

We will also add an error handling middleware to send errors in the API response received from multer. Use the following code:

const express = require("express");
const multer = require("multer");
const path = require("path");
const cors = require("cors");

const app = express();
const PORT = 4000;

app.use(cors());

app.use((req, _, next) => {
  console.log("Request received at:", req.path);
  next();
});

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/");
  },
  filename: function (req, file, cb) {
    cb(
      null,
      file.fieldname + "-" + Date.now() + path.extname(file.originalname)
    );
  },
});
const fileFilter = (req, file, cb) => {
  // Check if file is an image or video
  if (
    file.mimetype.startsWith("image/") ||
    file.mimetype.startsWith("video/")
  ) {
    cb(null, true);
  } else {
    cb(new Error("Only image and video files are allowed"));
  }
};

const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  limits: { fileSize: 2000000 },
});

app.post("/upload", upload.single("file"), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ message: "No file uploaded" });
  }
  res.status(200).json({
    message: "File uploaded successfully",
    filename: req.file.filename,
  });
});

app.use("/uploads", express.static("uploads"));

// Error handling middleware
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    // Multer error occurred (e.g., file size exceeded)
    return res.status(400).json({ message: err.message });
  } else if (err) {
    // Other errors (e.g., unsupported file type)
    return res.status(400).json({ message: err.message });
  }
  next();
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

We can observe our validation in action based on file size and type here:

That's it. We have a beautiful file upload component ready.

What's wrong with this approach?

We are currently storing files locally, but this won't work if our application becomes popular. We need a solution that can handle millions of files, terabytes of data, and hundreds of requests per second. The problem is even bigger with videos since they are large files and take a long time to upload. If our users are from different parts of the world, we need to run our backend in multiple regions to ensure a good user experience.

While a DIY approach is good for learning, it isn't practical for real-world applications. This is where ImageKit comes in. It is a third-party service that can manage all your file upload and storage needs. It is highly scalable and easily handles billions of files, terabytes of data, and hundreds of requests per second. Additionally, ImageKit provides features like image resizing, cropping, watermarking, file format conversion, video streaming, and much more.

Let's look at some common problems and possible solutions to make sure our upload system is robust and easy to use.

Limited File Storage on Servers

  • Files are stored on a file system, and they have limited disk space. Scaling up requires increasingly larger disks.
  • Local disks can only be attached to one server, preventing multiple application instances.
  • ImageKit handles globally distributed uploads. Manages billions of files, terabytes of data, and hundreds of requests per second.

High Latency with Large File Uploads

  • Large files like videos take longer to upload.
  • Users in different regions may experience high latencies when uploading to a single region.
  • We can deploy and run the backend in multiple regions to solve this.
  • ImageKit offers a global upload API endpoint. Routes upload requests to the nearest of seven strategically located regions to minimize latency.

Security Concerns

  • File uploads can be entry points for hackers to inject malicious files. This could compromise the entire cloud infrastructure.
  • Offload file handling to an external service to protect servers from attacks.

File Delivery

  • After uploading, delivering files to users requires another application layer.
  • ImageKit provides a robust delivery API and supports high request rates. It offers features such as intelligent image optimization, resizing, cropping, watermarking, file format conversion, video optimization, and streaming.

Uploading files to ImageKit directly from the browser

Let's do everything again, but this time using  ImageKit with a few lines of code. The overall functionality will remain the same. To ease the development, we will use ImageKit Angular SDK. In this case, we will upload files to  ImageKit directly from the browser.

We would be performing the following steps

  • Setup ImageKit Angular SDK
  • Setup routes and add a landing page
  • Generate authentication parameters on the backend
  • File picker component
  • Create a progress bar
  • Validation
  • Abort Upload

Setup ImageKit Angular SDK

Install the ImageKit Angular SDK:
npm install --save imagekitio-angular

Initializing the Angular SDK:
Before using the SDK, let's learn how to obtain the necessary initialization parameters:

  • urlEndpoint is a required parameter. This can be obtained from the URL-endpoint section or the developer section on your ImageKit dashboard.
  • publicKey and authenticator parameters are needed for client-side file upload. publicKey can be obtained from the Developer section on your ImageKit dashboard.
  • authenticator expects an asynchronous function that resolves with an object containing the necessary security parameters i.e signature, token, and expire.

💡

Note: Don't include your private key in any client-side code.

Create src/app/app.module.ts and add this code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ImagekitioAngularModule } from 'imagekitio-angular';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    ImagekitioAngularModule.forRoot({
      urlEndpoint: "your_endpoint",
      publicKey: "your_public_key",
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Update src/main.ts with the code below.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch((err) => console.log(err));

Setup routes and add a landing page

We'll use the component we created earlier. First, let's create a new component called landing-page. This will allow us to navigate to both native file upload and file upload using ImageKit. Use the command below to generate the component.

ng generate component landing-page

Now, update src/app/landing-page/landing-page.component.ts with code below.

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-landing-page',
  standalone: true,
  imports: [],
  templateUrl: './landing-page.component.html',
  styleUrl: './landing-page.component.css'
})
export class LandingPageComponent {
  constructor(private router: Router) {}
  navigateToNative() {
    this.router.navigate(['/native']);
  }
  navigateToImagekit(){
    this.router.navigate(['/imagekit']);
  }
}

In the file above, we created two functions: navigateToNative and navigateToImagekit. When called, these functions will navigate to /native and /imagekit, respectively.

Let's update its UI. Open src/app/landing-page/landing-page.component.html and add the code below. This will add two buttons which, on clicked, will call the navigateToNative and navigateToImagekit functions we created earlier.

<div class="container1">
  <div class="container2">
    <h1>Angular image & video upload</h1>
    <button (click)="navigateToNative()">Native Angular Upload</button>
    <button (click)="navigateToImagekit()">Imagekit Angular Upload</button>
  </div>
</div>

Next, we'll give our component some style by adding CSS to the src/app/landing-page/landing-page.component.css file. You can find the CSS code in this GitHub repository:

https://github.com/imagekit-samples/tutorials/blob/angular-file-upload/src/app/landing-page/landing-page.component.css

Now, we need to set up the routes for native and imagekit where we want our upload forms to render. Create src/app/app-routing.module.ts and add the following code.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UploadFormComponent } from './upload-form/upload-form.component';
import { LandingPageComponent } from './landing-page/landing-page.component';

const routes: Routes = [
  { path: '', component: LandingPageComponent },
  { path: 'native', component: UploadFormComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

We have configured our routes. Let’s add AppRoutingModule inside imports in src/app/app.module.ts.

...
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  ...
  imports: [
    ...
    AppRoutingModule,
    ...
  ],
  ...
})
export class AppModule { }

Now, we will update src/app/app.component.html to render our routes, as shown below.

<router-outlet></router-outlet>

Let’s update app.component.ts with the below code:

import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'imagekit-angular-app';
}

Now, if we navigate to http://localhost:4200/, it will look like this. You will notice that clicking the Native button takes us to the /native route and displays our previous upload form. However, when we click the ImageKit button, nothing happens because we haven't configured it yet.

Landing page
Landing page

Our landing page is ready.

Generate authentication parameters on the backend

To ensure security, the upload API requires authentication parameters so that no one else can upload files to your account. ImageKit uses a private key to generate these authentication parameters, ensuring secure uploads.

First, let's install the necessary packages.

cd server
npm install dotenv

Next, we'll add the /auth endpoint to our backend. Open server/server.js and update it with the following code.

...
const dotenv = require('dotenv');

dotenv.config();

const crypto = require("crypto");

const privateKey = process.env.PRIVATE_KEY;

...

app.get("/auth", function(req, res) {
  var token = req.query.token || crypto.randomUUID();
  var expire = req.query.expire || parseInt(Date.now()/1000)+2400;
  var privateAPIKey = `${privateKey}`;
  var signature = crypto.createHmac('sha1', privateAPIKey).update(token+expire).digest('hex');
  res.status(200);
  res.send({
      token : token,
      expire : expire,
      signature : signature
  });
});

...

We'll also need a .env file to specify the value for PRIVATE_KEY. You can find this key on the settings page of your ImageKit account, where you can also find the PUBLIC_KEY. A sample .env file should look like this:

PRIVATE_KEY=<your-private-key>

Let's run the backend server.

cd server
npm run start

You should see a log saying that the app is “Live on port 4000”.

If you GET http://localhost:4000/auth, you should see a JSON response like this. Actual values will vary.

{
    token: "5dd0e211-8d67-452e-9acd-954c0bd53a1f",
    expire: 1601047259,
    signature: "dcb8e72e2b6e98186ec56c62c9e62886f40eaa96"
}

File Picker Component

Next, we will create another component called imagekit-upload-form for uploading files using ImageKit. Use the command below to create the component.

ng generate component imagekit-upload-form

Let's create the UI for it. We will use the ik-upload component and authenticator function as well as a buttonRef property to use our custom upload form. Update src/app/imagekit-upload-form/imagekit-upload-form.component.html with the following code.

<div className="App">
  <h1>ImageKit Angular file upload</h1>
  <ik-upload [buttonRef]="myBtn" [authenticator]="authenticator">
  </ik-upload>
  <form class="upload-form">
    <h1>Upload File</h1>
    <button type="button" #myBtn>
      <i class="ph ph-upload"></i>
      <span>
        <span>Browse</span>
        your files
      </span>
    </button>
  </form>
</div>

Next, we'll give our component some style by adding CSS to the src/app/imagekit-upload-form/imagekit-upload-form.component.css file. You can find the CSS code in this GitHub repository:

https://github.com/imagekit-samples/tutorials/blob/angular-file-upload/src/app/imagekit-upload-form/imagekit-upload-form.component.css

Now, let's update src/app/imagekit-upload-form/imagekit-upload-form.component.ts. Here, we will create an authenticator function that fetches the authentication parameters required for the upload from /api/auth.

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

@Component({
  selector: 'app-imagekit-upload-form',
  templateUrl: './imagekit-upload-form.component.html',
  styleUrl: './imagekit-upload-form.component.css',
})
export class ImagekitUploadFormComponent {
  constructor() {}
  title = 'app';
  
  authenticator = async () => {
    try {
      // You can pass headers as well and later validate the request source in the backend, or you can use headers for any other use case.
      const response = await fetch('/api/auth');
      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(
          `Request failed with status ${response.status}: ${errorText}`
        );
      }
      const data = await response.json();
      const { signature, expire, token } = data;
      return { signature, expire, token };
    } catch (error: any) {
      throw new Error(`Authentication request failed: ${error.message}`);
    }
  };
}

Let's now configure the /imagekit route and render the imagekit-upload-form component on it.

Add the following inside the routes array in src/app/app-routing.module.ts:

{ path: 'imagekit', component: ImagekitUploadFormComponent }

We will also add the ImagekitUploadFormComponent to src/app/app.module.ts, as shown below.

...
import { ImagekitUploadFormComponent } from './imagekit-upload-form/imagekit-upload-form.component';

@NgModule({
  declarations: [
    AppComponent,
    ImagekitUploadFormComponent,
  ],
  
  ...
  
})
export class AppModule { }

If we navigate to http://localhost:4200/imagekit, we can see our file upload in action.

ImageKit handles the entire backend for us. Remember how we had to create an upload endpoint using multer earlier? ImageKit provides secure file uploads without the need for additional backend setup.

Create a Progress bar

Our file uploads are working properly. But that's not all ImageKit offers. We can use several event handlers like onError for upload errors and onSuccess for successful uploads events.

Let's create a progress bar to see these event handlers in action. Add the UI for progress bar update in src/app/imagekit-upload-form/imagekit-upload-form.component.html.

<div className="App">
  <h1>ImageKit Angular file upload</h1>
  <ik-upload
    (onSuccess)="handleUploadSuccess($event)"
    (onError)="handleUploadError($event)"
    [buttonRef]="myBtn"
    [authenticator]="authenticator"
    [onUploadStart]="onUploadStartFunction"
    [onUploadProgress]="onUploadProgressFunction"
  >
  </ik-upload>
  <form class="upload-form">
    <h1>Upload File</h1>

    <button type="button" #myBtn>
      <i class="ph ph-upload"></i>
      <span>
        <span>Browse</span>
        your files
      </span>
    </button>
    <div class="result" [style.display]="outputBoxVisible ? 'flex' : 'none'">
      <i class="ph ph-file"></i>
      <div class="file-details">
        <span class="file-name">{​{ fileName }​}</span>
        <ng-container
          *ngIf="uploadStatus === 200 || uploadStatus === undefined"
        >
          <div class="progress-bar">
            <div class="progress" [style.width]="progress"></div>
          </div>
          <span class="file-size">{​{ fileSize }​}</span>
        </ng-container>
      </div>

      <div
        class="upload-result"
        [style.display]="uploadStatus ? 'flex' : 'none'"
      >
        <span>{​{ uploadResult }​}</span>
        <ng-container *ngIf="uploadStatus === 200; else error">
          <i class="ph ph-check-circle"></i>
        </ng-container>
        <ng-template #error>
          <i class="ph ph-x-circle"></i>
        </ng-template>
      </div>
    </div>
  </form>
</div>

We can track the progress using onUploadStart and onUploadProgress events. Update src/app/imagekit-upload-form/imagekit-upload-form.component.ts  with the code below:

...
import { HTMLInputEvent } from 'imagekitio-angular/lib/utility/ik-type-def-collection';

...

export class ImagekitUploadFormComponent {
  outputBoxVisible = false;
  progress = `0%`;
  uploadResult = '';
  fileName = '';
  fileSize = '';
  uploadStatus: number | undefined;
  
  ...
  
  uploadErrorMessage = '';

  ...

  onUploadStartFunction = (event: HTMLInputEvent) => {
    this.outputBoxVisible = false;
    this.progress = `0%`;
    this.uploadResult = '';
    this.fileName = '';
    this.fileSize = '';
    this.uploadStatus = undefined;
    if (event.target?.files?.length) {
      const file: File = event.target?.files[0];
      this.fileName = file.name;
      this.fileSize = `${(file.size / 1024).toFixed(2)} KB`;
      this.outputBoxVisible = true;
    }
    console.log('onUploadStart');
  };

  onUploadProgressFunction = (event: ProgressEvent): void => {
    const progress = (event.loaded / event.total) * 100;
    this.progress = `${Math.round(progress)}%`;
    console.log('progressing', { progress: this.progress });
  };

  handleUploadSuccess = (res) => {
    console.log('File upload success with response: ', res);

    if (res.$ResponseMetadata.statusCode === 200) {
      this.uploadResult = 'Uploaded';
      this.outputBoxVisible = true;
    }
    this.uploadStatus = res.$ResponseMetadata.statusCode;
  };

  handleUploadError = (err) => {
    console.log('There was an error in upload: ', err);
    this.uploadErrorMessage = 'File upload failed.';
    this.uploadResult = 'File upload failed!';
  };
}

It should look like this:

ImageKit file upload
ImageKit file upload

All the parameters supported by the ImageKit Upload API can be passed as shown above (e.g. extensions, webhookUrl, customMetadata etc).

Validation

To add validation, we will use the validateFile property of ik-upload. Let’s add the code below to src/app/imagekit-upload-form/imagekit-upload-form.component.html in the ik-upload component.

[validateFile]="validateFileFunction"

Now add the code below to src/app/imagekit-upload-form/imagekit-upload-form.component.ts. Here, we have created the validateFileFunction, which validates a file based on size and file type.

validateFileFunction = (file: File): boolean => {
  console.log('validating', file);
  if (file.size > 2000000) {
    // Less than 2mb
    this.fileName = file.name;
    this.outputBoxVisible = true;
    this.uploadStatus = 413;
    this.uploadResult = 'File size should be less than 2mb';
    return false;
  }
  if (!(file.type.startsWith('image/') || file.type.startsWith('video/'))) {
    this.fileName = file.name;
    this.outputBoxVisible = true;
    this.uploadStatus = 403;
    this.uploadResult = 'Only image and video files are allowed';
    return false;
  }
  return true;
};

This is how our validation works.

Abort Upload

Let’s see how we can abort the file upload after it is initiated. Add the code below to  src/app/imagekit-upload-form/imagekit-upload-form.component.ts.

...
import { IkUploadComponent } from 'imagekitio-angular';

...

@ViewChild('upload') uploadComponent: IkUploadComponent | undefined;

...

onAbortFunction = () => {
  this.uploadComponent && this.uploadComponent.abort();
};
...

Now, we will add an abort button in the UI. It will be visible only once the upload has started and will disappear once it is done. Add this code to src/app/imagekit-upload-form/imagekit-upload-form.component.html

<div className="App">
  <h1>ImageKit Angular file upload</h1>
  <ik-upload
    #upload
    ...
  >
  </ik-upload>
  
  ...
  
    <ng-container *ngIf="outputBoxVisible && uploadStatus === undefined">
      <div class="result-container">
        <button (click)="onAbortFunction()" type="button">Abort</button>
      </div>
    </ng-container>
  </form>
</div>

💡

This only cancels the XHR request on the frontend. If the file was uploaded but the final response wasn’t received, the file might still appear in the Media Library after clicking abort. This is more common with smaller files.

We can see below that clicking on the abort button has stopped the upload.

ImageKit abort upload
ImageKit abort upload

That's it. We have a beautiful file upload component ready.

Bonus

As we saw, the ImageKit ik-upload component made file uploads much easier. Similarly, the ImageKit Angular SDK provides other useful components like ik-image for rendering images and ik-video for video playback.

Let's see how ImageKit makes video playback simpler. Check out the code below. It will render a video from the specified video path for a particular account. For more details on ImageKit Angular features, check out our imagekit angular guide.

  <ik-video
    urlEndpoint="url-endpoint"
    class="ikvideo-default"
    [path]="video-path"
    [transformation]="transformation"
    controls="true"
  >
  </ik-video>

We have defined a transformation as shown below.

transformation: Array<Transformation> = [
  {
    height: '200',
    width: '200',
  },
];

Here's how it will look: the ik-video component renders a video tag with the applied transformations.

ImageKit angular video
ImageKit angular video

Conclusion

In this tutorial, we learned how to create a beautiful file upload component in Angular and how to use ImageKit's free upload API and Angular SDK to do everything with just a few lines of code.