2017-10-04 50 views
2

我的任務是使用auth頭進行異步圖像請求。我有這樣的圖像路徑:角度4圖像異步與承載頭

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

而且我需要添加持票人令牌頭的這種請求。頁面包含許多圖像,所以Ajax請求不適合。 不知道該怎麼做。

+0

爲什麼你認爲XHR-s不適合這個? –

+0

@JánHalaša - 你是什麼意思?我認爲可能在Angular 4中存在這個問題的一些默認的東西,我的意思是Resful API項目 –

回答

2

現在,沒有辦法通過html中的標籤進行授權調用,瀏覽器不提供此API的API,因此您必須提出XHR請求。這是一個解決方法:通過XHR獲取圖像,將其轉換爲blob,然後將blob轉換爲base64並將圖像插入到標記的src中。這個解決方案需要兩個管道清楚:一個是用於製作XHR呼叫的定製管道,另一個是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調用期間提供: 你需要記住enter image description here 一件事是CORS:你的形象服務,服務器應該以一種方式配置,即它接受XHR對Angular應用程序運行的域名圖像的調用,還需要爲自定義管道提供絕對路徑,否則會向Angular應用程序的域本身發出請求。

+0

這不適用於Angular 4.它必須使用HttpClient的已棄用版本? – Charles

-1

如果您已經爲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

這不起作用。它必須使用HttpClient的已棄用版本,因爲switchMap似乎不再存在,或者至少不會按原樣編譯。而且,它不承認在這裏有一行有兩個異步進程:http.get和readAsDataURL。 – Charles

0

假設你已經實施了HttpIntercepter添加標題,下面是實際做的工作(在角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獲得的observable!我不知道爲什麼以前的解決方案假設你可以。 http.get的observable指示何時從服務器檢索數據。但是,之後還有另一個異步進程需要完成:調用reader.readAsDataURL。因此,您需要創建一個Observable,然後在該過程完成後再調用它。另外,如果您不立即將某些內容放入圖像中,瀏覽器仍然會嘗試使用HTTP加載圖像,並且在JavaScript控制檯中出現錯誤。這是第一位觀察者的原因。接下來的電話會放入一張空白的小GIF圖像。

這種方法的一個問題是,如果圖像被多次使用,它會每次加載每一個圖像。即使瀏覽器緩存,您每次都會轉換爲base64。我創建了一個緩存來存儲圖像,以便在第一個之後不再需要將來的查詢。