Progress of Download in Angular using RxJs

I have successfully implemented a solution in Angular 13 / RxJs 7 for tracking download progress.

To start, I created some enums:

export enum RequestType {
  get = 'GET',
  post = 'POST',
  put = 'PUT',
  delete = 'DELETE',
}

export enum ActionType {
  download = 1,
  upload
}

Next, I developed a shared service to manage progress tracking:

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

  percentDone = 0;
  actionType: ActionType;

  constructor(private http: HttpClient) { }

  downloadFile(uri: string, requestType: RequestType, actionType: ActionType, body: any): Observable<number> {

    this.actionType = actionType;

    const req = new HttpRequest(requestType, uri, body, {
      reportProgress: true,
      responseType: 'blob'
    });

    return this.http
      .request(req)
      .pipe(
        map(event => this.getPercentage(event)),
    );

  }

  public getPercentage(event: HttpEvent<any>): number {

    switch (event.type) {

      case HttpEventType.UploadProgress:

      // Ignore upload progress for download actions
      if (this.actionType !== ActionType.upload) {
        return 0;
      }

      // Calculate and display % completion:
        if (event && event.total) {
          this.percentDone = Math.round(100 * event.loaded / event.total);
          return this.percentDone;
        } else {
          return 0;
        }

      case HttpEventType.DownloadProgress:

        if (event && event.total) {
          this.percentDone = Math.round(100 * event.loaded / event.total);
        }

        return this.percentDone;

      default:

        // Irrelevant event type
        return this.percentDone;

    }
  }

}

Then, I subscribed to the service to monitor the download progress:

this.downloadService
  .downloadFile(url, RequestType.post, ActionType.download, body)
  .subscribe(progress => this.progress = progress);

Everything is functioning well, and I can view real-time progress in the component library's progress bar.

Now, my question is ... how do I access the downloaded file?

Answer №1

Instead of simply returning the progress, consider returning an object with both the progress percentage and the HTTP response.

return this.http.request(req).pipe(
  map(event => ({
    progress: this.getPercentage(event),
    response: event
  })),
);

Now, when you subscribe, you can access the object like this:

this.downloadService
  .downloadFile(url, RequestType.post, ActionType.download, body)
  .subscribe({
    next: ({progress, response}) => {
      this.progress = progress;
      // utilize the response data
    },
    error: (error: any) => {
      // handle any errors that occur
    }
  });

Answer №2

Perhaps this could be of assistance to someone Check out the solution here

I built upon this example by adding a feature to calculate the percentage of the downloaded file.

  1. Ensure that your server sends content as a Blob (binary)
  2. The server must also include the header
    Content-length: 789(other count file size in bytes)
    in the request headers. It took me a while to figure out what was happening on the server and what information I was missing about the file size.

In the provided example, you won't visually see the progress due to the small file size. However, if you open the DevTools and set throttling, you will be able to see it. For larger files, you'll notice the download progress being calculated in percentage as the packages are split and sent in chunks.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the Power of TailwindCss in Storybook 6 for Angular Development

I am in the process of creating a component library using Angular version 11.2.8, and I'm attempting to integrate TailwindCss and Storybook 6. Despite trying various configurations, none seem to be working correctly for me. Whenever I run Storybook, ...

failure of pipe during search for art gallery information

Hi, I've created a filter pipe to search for imagenames and imageids among all my images. However, it seems to only find matches in the first image. There seems to be something wrong with my code. This is my FilterPipe class in filter.pipe.ts where I ...

With *ngFor in Angular, radio buttons are designed so that only one can be selected

My goal is to create a questionnaire form with various questions and multiple choice options using radio buttons. All the questions and options are stored in a json file. To display these questions and options, I am utilizing nested ngFor loops to differ ...

Different types of TypeScript interface keys being derived from an enum

How can I efficiently implement a list of properties to be used as keys in interfaces with different types? I want to restrict the use of properties that do not exist in an enum. export enum SomeProperties { prop1, prop2, prop3 }; export interface ...

Utilizing Sequelize with Typescript for referential integrity constraints

After defining these two Sequelize models: export class Users extends Model<Users> { @HasMany(() => UserRoles) @Column({ primaryKey: true, allowNull: false, unique: true }) UserId: string; @Column({ allowNull: false, unique: tru ...

Splitting large components into smaller components

I am currently in the process of splitting the code from index.tsx into two separate files: firstTab.tsx and secondTab.tsx. As of now, I have only separated the code related to the first tab into firstTab.tsx, which can be viewed in the code editor. The co ...

Dynamically passing output placeholders through ng-content in an Angular component template

Can a component handle dynamic projection in this way? <my-component [date]="2022-03-22"> <p>This particular date falls on a <strong #dayName></strong>! It is in week number <span #weekNum></span> of year &l ...

What is the process for automatically initiating a service when importing a module in Angular?

I am curious about how I can automatically run a service within a module upon importing it, without the need for manual service injection and execution. This functionality is similar to how the RouterModule operates. @NgModule({ imports: [ Browser ...

The Angular Component I've created is displaying a single set of data on each mat-tab screen

I have developed a component with the file name hukamnama-sahib.component.html, which looks like this: <body *ngFor="let dataitem of HukamnamaSahibList"> <h4> <span class="gurmukhi">{{dataitem.line.gurmukhi.unico ...

Using ngIf for binding

Trying to bind values based on conditions specified in *ngIf. However, when using the && operator within *ngIf, it seems to be behaving mysteriously. Sample Code: <div *ngIf="days.sunday == true"> <p class="circle ml-3" ...

The term 'ItemIsLoading' is typically used as a type, however, it is being incorrectly used as a value in this context

Currently, I am in the process of developing a typescripted Redux store for a react project. While the Interfaces are functioning correctly, I encountered an error when attempting to pass them within the exports themselves. The error message states "' ...

Unexpected identifier error: Typescript interface name syntax is incorrect

I am currently learning Typescript and still navigating my way through it. I have extensively searched for a solution to the issue I am facing, but couldn't find one, hence I am seeking help here. The problem lies in a SyntaxError at the interface nam ...

The 'get' property in the class 'ToastInjector' cannot be assigned to the 'get' property in its base class 'Injector'

error TS2416: The property 'get' in the type 'ToastInjector' cannot be assigned to the same property in the base type 'Injector'. The type '(token: any, notFoundValue?: T, flags?: InjectFlags) => ToastPackage | T&apos ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

Can you explain the concept of TestBed in Jasmine?

Recently, I have started using Jasmine with Angular 2 and have encountered an issue while working with the TestBed object in my test cases. The error message reads as follows: Please call "TestBed.compileComponents" before your test. Can anyone advise on ...

Struggling with repeatedly traversing characters in a string to solve the Palindrome challenge

I am working on a recursive solution for a Palindrome problem, but it seems that my code is only considering the first recursive call instead of the second one which should analyze all characters in the string. I suspect there might be a logical error in ...

Null value returned by getDOM() function during transition from Angular 2 to Angular 4

I've encountered a unique challenge that has me stumped. Despite finding similar issues, the proposed solutions don't seem to work for my case. My issue arose when I decided to migrate a project from Angular 2 to Angular 4. I began by creating a ...

The process of uploading an Angular application to a server with an altered baseHref, resulting in the

I recently posted a question and had some discussions in the comments. Here is the link to the previous post for reference: Angular base href not showing up in URL In summary, my application works fine locally, with correct routing between localhost:4200/ ...

Encountering a promise error when using TypeScript and React Lazy

I've been working with React and TypeScript, incorporating a higher order component to verify user authentication. However, after implementing the hoc, I encountered an error in my routes: /home/nidhin/Documents/Nidhinbackup/F/iot-remsys-demotwo/rem ...

Update the component to display the latest information from the Bryntum grid table

In the Vue component, I have integrated a Bryntum grid table along with a bar chart. Clicking on one of the bars in the chart should update the data displayed in the Bryntum grid table. However, I've encountered difficulty in reloading the entire Bryn ...