2017-10-04 20 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。我创建了一个缓存来存储图像,以便在第一个之后不再需要将来的查询。