私の仕事は、authヘッダーを使用して非同期画像リクエストを行うことです。私はこのような画像パスを持っています:
<img src="{{file.src}}"/>
そして、私はそのようなリクエストのヘッダーにベアラートークンを追加する必要があります。ページには多くの画像が含まれているため、ajaxリクエストは適合しません。これを行う方法がわかりません。
ヘッダーを追加するために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への変換を行います。画像を保存するキャッシュを作成したので、最初のクエリの後で今後のクエリは必要ありません。
現在、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ヘッダーが提供されていることがわかります。 注意する必要がある1つのことはCORSです。イメージサービングサーバーは、Angularアプリが実行されているドメインからのイメージに対するXHR呼び出しを受け入れるように構成する必要があります。また、カスタムパイプに絶対URLを提供する必要があります。そうしないと、Angularアプリのドメイン自体にリクエストが送信されます。
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);
}
}
この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() { }};
});
}
}