rxjs возвращает результат события внутри карты - PullRequest
0 голосов
/ 16 января 2019

надеюсь, что кто-то может помочь, у меня есть наблюдаемая, которая возвращает строку base64 внутри карты запроса http ниже

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val => {
                if (!asBase64) {
                    return this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val))
                }
                let base64Data = null;
                let reader = new FileReader();
                reader.readAsDataURL(val);
                reader.onloadend = () => {
                    base64Data = reader.result; // need to return this! 
                    console.log(base64Data);

                };
            }));
    }

Проблема в том, что мне нужно условно вернуть результат reader.onloadend = () => {

Полагаю, мне нужно преобразовать это в обещание и использовать mergeMap? Не уверен как.

Обновление

Так что еще один удар по этому ... Из документации mergeMap похоже, что это должно работать, отладка. Я вижу, как большой двоичный объект передается в reader.readAsDataURL(data);, но return reader.result; никогда не срабатывает, похоже, я теряю ссылку на reader

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val =>
                <any>(asBase64 ? val : this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)))
            )).pipe(mergeMap(data => {
                if (!asBase64) {
                    return of(data)
                } else {
                    let reader = new FileReader();
                    let eventObservable = fromEvent(reader, 'onloadend').pipe(map(() => {
                        return reader.result;
                    }));
                    reader.readAsDataURL(data);
                    return eventObservable;
                }
            }));
    }

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Проблема в том, что событие FileReader onLoadEnd является асинхронным, поэтому оно не будет работать внутри функции карты.

Один из способов решения вашей проблемы - обернуть вашу функцию Обещанием, создать наблюдаемое из обещания с помощью из и использовать оператор switchMap , чтобы сопоставить наблюдаемое с новый от обещания.

Теперь, когда вы подпишетесь на полученную наблюдаемую информацию, она выдаст результат из FileReader. Если он отклоняется, вы можете получить ошибку со вторым параметром метода подписки.

import {from} from 'rxjs'
import {switchMap} from 'rxjs/operators'

transform(url: string, asBase64: boolean): Observable<any> {  
  return this.http
    .get(url, {responseType: 'blob'})
    .pipe(
      switchMap(
        val => from( // create the observable from a promise
          new Promise((resolve, reject) => { //create a new Promise               
            if (!asBase64) {
                resolve(this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val))) //resolve if base64
            }            
            const reader = new FileReader();
            reader.readAsDataURL(val);

            reader.onloadend = () => resolve(reader.result); //resolve when it finishes to load the file
            reader.onerror = () => reject(reader.error); //rejects if there was an error while reading the file
          })
        )
      )
    );
}

...
const file$ = transform(url, asBase64)
file$.subscribe(
   (file) => console.log(file), // do stuff with the file here
   (error) => console.log(error)  // there was an error while reading the file
)

0 голосов
/ 16 января 2019

Хорошо, понял это в конце. Поэтому мне нужно было использовать mergeMap на основе ответа, а при желании asBase64 создать новую наблюдаемую из события onloadend чтения файлов.

Но это также не сработало из-за этих строк (см. Код в моем исходном вопросе).

let reader = new FileReader();
                    let eventObservable = fromEvent(reader, 'onloadend').pipe(map(() => {
                        return reader.result;
                    }));
                    reader.readAsDataURL(data);
                    return eventObservable;

Проблема с подходом fromEvent состоит в том, что событие запускается только один раз, и в моем случае оно срабатывало до того, как мой шаблон Angular сумел подписаться на наблюдаемое, решение состояло в том, чтобы использовать Subject, не уверенный, что это идеально в этом случае. дело, но мне кажется, нормально.

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val =>
                <any>(asBase64 ? val : this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)))
            )).pipe(mergeMap(data => {
                if (!asBase64) {
                    return of(data)
                } else {
                    let reader = new FileReader();
                    let subject = new Subject<any>();
                    reader.onloadend = () => {
                        subject.next(reader.result);
                    };
                    reader.readAsDataURL(data);
                    return subject;
                }
            }));
    }
...