web-dev-qa-db-ja.com

Angular 4ベアラヘッダーと非同期の画像

私の仕事は、authヘッダーを使用して非同期画像リクエストを行うことです。私はこのような画像パスを持っています:

<img src="{{file.src}}"/>

そして、私はそのようなリクエストのヘッダーにベアラートークンを追加する必要があります。ページには多くの画像が含まれているため、ajaxリクエストは適合しません。これを行う方法がわかりません。

7
Katya Makeeva

ヘッダーを追加するためにHttpIntercepterを実装したと仮定すると、実際に機能するソリューションは次のとおりです(Angular 4)の場合)。

import { Pipe, PipeTransform } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Pipe({
  name: 'secure'
})
export class SecurePipe implements PipeTransform {

  constructor(private http: HttpClient) { }

  transform(url: string) {

    return new Observable<string>((observer) => {
      // This is a tiny blank image
      observer.next('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');

      // The next and error callbacks from the observer
      const {next, error} = observer;

      this.http.get(url, {responseType: 'blob'}).subscribe(response => {
        const reader = new FileReader();
        reader.readAsDataURL(response);
        reader.onloadend = function() {
          observer.next(reader.result);
        };
      });

      return {unsubscribe() {  }};
    });
  }
}

次のように使用します。

<img [src]="'/api/image/42' | secure | async" />

以前のソリューションには、かなり劇的な欠陥がありました。私はこれが完璧であることを保証しませんが、実際にテストされ、私のために働いています。

Http.getから取得したオブザーバブルを返すことはできません!以前のソリューションがあなたができると仮定する理由がわかりません。 http.getのオブザーバブルは、データがサーバーから取得されるタイミングを示します。ただし、その後に完了する必要がある別の非同期プロセスがあります。reader.readAsDataURLの呼び出しです。したがって、そのプロセスが完了した後に次に呼び出すObservableを作成する必要があります。

また、画像に何かをすぐに入れない場合でも、ブラウザーはHTTPを使用して画像をロードしようとし、JavaScriptコンソールでエラーが発生します。これが、空の小さなGIF画像を配置する最初のobserver.next呼び出しの理由です。

このアプローチの問題は、イメージが複数回使用される場合、毎回ロードされることです。ブラウザがキャッシュする場合でも、毎回base64への変換を行います。画像を保存するキャッシュを作成したので、最初のクエリの後で今後のクエリは必要ありません。

8
Charles

現在、htmlのタグを介してのみ承認済み呼び出しを行う方法はありません。ブラウザはこれのためのAPIを提供していないため、XHRリクエストを行う必要があります。これは回避策です:XHR経由で画像を取得し、それをblobに変換してから、blobをbase64に変換し、画像をタグのsrcに挿入します。このソリューションでは、2つのパイプを明確にする必要があります。1つはXHR呼び出しを行うためのカスタムパイプで、もう1つはAngularの組み込みパイプasyncです。これがカスタムパイプです。

import { Pipe, PipeTransform } from '@angular/core';
import { Http, RequestOptions, Headers, ResponseContentType } from @angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';

@Pipe({name: 'image'})
export class ImagePipe implements PipeTransform {
  constructor(private http: Http) {}

  transform(url: string) {
  const headers = new Headers({'Authorization': 'MY TOKEN', 'Content-Type': 'image/*'}); /* tell that XHR is going to receive an image as response, so it can be then converted to blob, and also provide your token in a way that your server expects */
  return this.http.get(url, new RequestOptions({headers: headers, responseType: ResponseContentType.Blob})) // specify that response should be treated as blob data
  .map(response => response.blob()) // take the blob
  .switchMap(blob => {
  // return new observable which emits a base64 string when blob is converted to base64
      return Observable.create(observer => { 
        const  reader = new FileReader(); 
        reader.readAsDataURL(blob); // convert blob to base64
        reader.onloadend = function() {             
              observer.next(reader.result); // emit the base64 string result
        }
      });
    });
  }
}

そして、これがあなたのhtmlになります:

<img [src]="('https://www.w3schools.com/css/trolltunga.jpg' | image) | async" />

パイプを使用して、base64文字列のオブザーバブルを取得し、asyncを使用して、実際に発行された文字列をsrcタグ内に挿入します。

Networkタブを見ると、XHR呼び出し中にAuthorizationヘッダーが提供されていることがわかります。 enter image description here 注意する必要がある1つのことはCORSです。イメージサービングサーバーは、Angularアプリが実行されているドメインからのイメージに対するXHR呼び出しを受け入れるように構成する必要があります。また、カスタムパイプに絶対URLを提供する必要があります。そうしないと、Angularアプリのドメイン自体にリクエストが送信されます。

4
Armen Vardanyan

APIにHttpInterceptorをすでに実装している場合は、インターセプターにヘッダーを処理させることにより、上記のPipeコードを簡略化できます。以下は、HttpClientを使用した更新バージョンです。

@Pipe({
  name: 'image',
})
export class ImagePipe implements PipeTransform {
  constructor(
    private http: HttpClient,
    private config: ConfigProvider
  ) { }

  transform(url: string) {
    return this.http.get(url, {responseType: "blob"})
      .switchMap(blob => {
        // return new observable which emits a base64 string when blob is converted to base64
        return Observable.create(observer => {
          const reader = new FileReader();
          reader.readAsDataURL(blob); // convert blob to base64
          reader.onloadend = function () {
            observer.next(reader.result); // emit the base64 string result
          }
        });
      });
  }
}
`

そして、これが例 interceptor です:

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private config: ConfigProvider) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

      const authReq = req.clone({
        setHeaders: {
          Authorization: this.config.getAuthorization(),
          'X-App-ClientId': this.config.authentication['X-App-ClientId']
        }
      });
      return next.handle(authReq);
    }
}
0
Ilkka Nisula

このAngular 5のソリューションと、Armen VardanyanとCharlesのソリューションの組み合わせです。ArmenのソリューションはAngular 5で機能しますが、最初にロードしようとします http:// localhost/null url。それを解決するために、チャールズの小さな空白の画像を含めました:

@Pipe({name: 'secure'})
export class SecurePipe implements PipeTransform {
  constructor(private http: Http,
    public g: BaseGroupService) {}

    transform(url: string) {

      return new Observable<string>((observer) => {
        observer.next('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
        const {next, error} = observer;

        const headers = new Headers({'Authorization': 'TOKEN', 'Content-Type': 'image/*'}); 
        this.http.get(url, new RequestOptions({headers: headers, responseType: ResponseContentType.Blob})).subscribe(response => {
          const reader = new FileReader();
          reader.readAsDataURL(response.blob());
          reader.onloadend = function() {
            observer.next(reader.result);
          };
        });
        return {unsubscribe() {  }};
      });
    }
}
0
Vil