0

I had to convert a local audio for my angular project. The only way I found in order to do that is through XMLHttpRequest :

var xhr = new XMLHttpRequest();       
    xhr.open("GET", "/path/to/local/image/file", true); 
    xhr.responseType = "blob";
    xhr.onload = function (e) {
            console.log(this.response);
            var reader = new FileReader();
            reader.onload = function(event) {
               var res = event.target.result;
               console.log(res)
            }
            var file = this.response;
            reader.readAsDataURL(file)
    };
    xhr.send()

"res" is giving me the base64 for my audio. However I'm not able to retrieve res and store it in a variable to use it in another function. How can I do that ? And is there a better way than going with XMLHttpRequest ? Thanks in advance.

yorozuya
  • 15
  • 4

1 Answers1

0

I'd suggest you to use Angular HTTP Module. It's Angular native and as such easier to integrate with other parts of the application.

You could then use a container (like RxJS ReplaySubject with buffer 1) to cache/hold the values from the HTTP request and the subsequent FileReader's onload callback.

Note: since this is all asynchronous, any statements that directly depend on the result from the HTTP request must be inside the subscription. See here for more info on how async data works.

app.module.ts

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { HttpClientModule } from "@angular/common/http";

import { AppComponent } from "./app.component";
import { DataService } from './data.service';

@NgModule({
  imports: [BrowserModule, FormsModule, HttpClientModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [DataService]
})
export class AppModule {}

data.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";    // <-- import `HttpClient`

import { Observable, ReplaySubject } from 'rxjs';

@Injectable()
export class DataService {
  private fileSource: ReplaySubject<any> = new ReplaySubject<any>(1);
  public file$: Observable<any> = this.fileSource.asObservable();

  constructor(private http: HttpClient) {             // <-- inject `HttpClient`
    this.getFile();                                   // <-- trigger file fetch here
  }

  getFile(): void {
    this.http.get('/path/to/local/image/file', { responseType: "blob" }).subscribe({    // <-- add `responseType`
      next: (response: any) => {
        const reader = new FileReader();
        reader.onload = (event) => {                  // <-- arrow function
           this.fileSource.next(event.target.result); // <-- push to the `ReplaySubject`
        }
        reader.readAsDataURL(response)                // <-- should NOT be `this.response`
      },
      error: (error: any) => {
        // handle error
      }
    });
  }
}

app.component.ts

import { Component, OnInit, OnDestroy } from "@angular/core";

import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";

import { DataService } from "./data.service";

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  close$ = new Subject<any>();
  file: any;

  constructor(private _data: DataService) { }

  ngOnInit() {
    this._data.file$.pipe(
      takeUntil(this.close$)
    ).subscribe({
      next: (file: any) => {
        this.file = file;
        // other statements that depend on `this.file`
      }
    });
  }

  ngOnDestroy() {
    this.close$.next();           // <-- close open subscriptions
  }
}

Update

  • Add responseType to GET request
  • Working example: Stackblitz
ruth
  • 29,535
  • 4
  • 30
  • 57
  • Hello, thank you for your answer. However I'm not able to make it work and not getting any error. I looked with the debbuger : it's not entering the subscription – yorozuya Mar 17 '21 at 08:50
  • @yorozuya: There are two subscriptions. Which subscription does it not enter? – ruth Mar 17 '21 at 09:21
  • @yorozuya: Do you trigger the request in the service's constructor like shown? Also do you see the request executing without any errors in the browser's network page? – ruth Mar 17 '21 at 13:15
  • yes I do trigger it from the constructor and there is no errors – yorozuya Mar 17 '21 at 13:25
  • @yorozuya: One error I could see is this line: `reader.readAsDataURL(this.response)`. It should be `reader.readAsDataURL(response)`. I've updated the code. Please see it works now. In any case the subscription's `next` callback should be triggered if there are no errors in the request. – ruth Mar 17 '21 at 13:29
  • @yorozuya: The `responseType: 'blob'` need to added for blob files in the GET request. The default is `json`. I've updated the answer and included a working example. – ruth Mar 17 '21 at 13:42
  • Thank you so so much Michael, it's working and you explained very well ! – yorozuya Mar 17 '21 at 14:57