Angular 2 скачать PDF из API и отобразить его в представлении
Я изучаю Angular 2 бета. Интересно, как загрузить PDF файл из API и отобразить его на мой взгляд? Я попытался сделать запрос, используя следующее:
var headers = new Headers();
headers.append('Accept', 'application/pdf');
var options = new ResponseOptions({
headers: headers
});
var response = new Response(options);
this.http.get(this.setUrl(endpoint), response).map(res => res.arrayBuffer()).subscribe(r=>{
console.log(r);
})
- Обратите внимание, что я использую только
console.log
, чтобы увидеть значение r
Но я всегда получаю следующее сообщение об исключении:
"arrayBuffer()" метод, не реализованный в суперклассе Response
Это потому, что этот метод еще не готов в Angular 2 Beta? Или есть какая-то ошибка, которую я сделал?
Любая помощь будет оценена по достоинству. Большое вам спасибо.
Ответы
Ответ 1
На самом деле, эта функция еще не реализована в поддержке HTTP.
В качестве временного решения, вам нужно расширить BrowserXhr
класс Angular2, как описано ниже, чтобы установить responseType
к blob
на базовый объект XHR:
import {Injectable} from 'angular2/core';
import {BrowserXhr} from 'angular2/http';
@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr = super.build();
xhr.responseType = "blob";
return <any>(xhr);
}
}
Затем вам нужно обернуть полезную нагрузку ответа в объект Blob
и использовать библиотеку FileSaver, чтобы открыть диалоговое окно загрузки:
downloadFile() {
this.http.get(
'https://mapapi.apispark.net/v1/images/Granizo.pdf').subscribe(
(response) => {
var mediaType = 'application/pdf';
var blob = new Blob([response._body], {type: mediaType});
var filename = 'test.pdf';
saveAs(blob, filename);
});
}
Библиотека FileSaver должна быть включена в ваш HTML файл:
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
Смотрите этот plunkr: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview
К сожалению, это установит responseType
для всех запросов AJAX. Чтобы иметь возможность установить значение этого свойства, в XHRConnection
и Http
нужно сделать больше обновлений.
В качестве ссылок смотрите эти ссылки:
редактировать
Подумав немного больше, я думаю, что вы могли бы использовать иерархические инжекторы и настроить этого поставщика только на уровне компонента, который выполняет загрузку:
@Component({
selector: 'download',
template: '<div (click)="downloadFile() ">Download</div>'
, providers: [
provide(CustomBrowserXhr,
{ useClass: CustomBrowserXhr }
]
})
export class DownloadComponent {
@Input()
filename:string;
constructor(private http:Http) {
}
downloadFile() {
this.http.get(
'https://mapapi.apispark.net/v1/images/'+this.filename).subscribe(
(response) => {
var mediaType = 'application/pdf';
var blob = new Blob([response._body], {type: mediaType});
var filename = 'test.pdf';
saveAs(blob, filename);
});
}
}
Это переопределение применимо только к этому компоненту (не забудьте удалить соответствующее обеспечение при загрузке приложения). Компонент загрузки может быть использован так:
@Component({
selector: 'somecomponent',
template: '
<download filename="'Granizo.pdf'"></download>
'
, directives: [ DownloadComponent ]
})
Ответ 2
Итак, вот как мне удалось заставить его работать.
Моя ситуация: мне нужно было загрузить PDF файл из конечной точки API и сохранить результат в формате PDF в браузере.
Чтобы поддерживать сохранение файлов во всех браузерах, я использовал модуль FileSaver.js.
Я создал компонент, который принимает ID файла для загрузки в качестве параметра.
Компонент, называется так:
<pdf-downloader no="24234232"></pdf-downloader>
Сам компонент использует XHR для извлечения/сохранения файла с номером, указанным в параметре no. Таким образом, мы можем обойти тот факт, что модуль Angular2 http еще не поддерживает двоичные типы результатов.
И теперь, без лишнего шума, код компонента:
import {Component,Input } from 'angular2/core';
import {BrowserXhr} from 'angular2/http';
// Use Filesaver.js to save binary to file
// https://github.com/eligrey/FileSaver.js/
let fileSaver = require('filesaver.js');
@Component({
selector: 'pdf-downloader',
template: `
<button
class="btn btn-secondary-outline btn-sm "
(click)="download()">
<span class="fa fa-download" *ngIf="!pending"></span>
<span class="fa fa-refresh fa-spin" *ngIf="pending"></span>
</button>
`
})
export class PdfDownloader {
@Input() no: any;
public pending:boolean = false;
constructor() {}
public download() {
// Xhr creates new context so we need to create reference to this
let self = this;
// Status flag used in the template.
this.pending = true;
// Create the Xhr request object
let xhr = new XMLHttpRequest();
let url = `/api/pdf/iticket/${this.no}?lang=en`;
xhr.open('GET', url, true);
xhr.responseType = 'blob';
// Xhr callback when we get a result back
// We are not using arrow function because we need the 'this' context
xhr.onreadystatechange = function() {
// We use setTimeout to trigger change detection in Zones
setTimeout( () => { self.pending = false; }, 0);
// If we get an HTTP status OK (200), save the file using fileSaver
if(xhr.readyState === 4 && xhr.status === 200) {
var blob = new Blob([this.response], {type: 'application/pdf'});
fileSaver.saveAs(blob, 'Report.pdf');
}
};
// Start the Ajax request
xhr.send();
}
}
Я использовал шрифт Awesome для шрифтов, используемых в шаблоне. Я хотел, чтобы компонент отображал кнопку загрузки и счетчик, в то время как PDF файл был извлечен.
Кроме того, обратите внимание, что я могу использовать require для извлечения модуля fileSaver.js. Это потому, что я использую WebPack, поэтому я могу потребовать/импортировать, как я хочу. Ваш синтаксис может отличаться в зависимости от вашего инструмента построения.
Ответ 3
Я не думаю, что все эти хаки необходимы. Я просто сделал быстрый тест со стандартным сервисом http в angular 2.0, и он работал, как ожидалось.
/* generic download mechanism */
public download(url: string, data: Object = null): Observable<Response> {
//if custom headers are required, add them here
let headers = new Headers();
//add search parameters, if any
let params = new URLSearchParams();
if (data) {
for (let key in data) {
params.set(key, data[key]);
}
}
//create an instance of requestOptions
let requestOptions = new RequestOptions({
headers: headers,
search: params
});
//any other requestOptions
requestOptions.method = RequestMethod.Get;
requestOptions.url = url;
requestOptions.responseType = ResponseContentType.Blob;
//create a generic request object with the above requestOptions
let request = new Request(requestOptions);
//get the file
return this.http.request(request)
.catch(err => {
/* handle errors */
});
}
/* downloads a csv report file generated on the server based on search criteria specified. Save using fileSaver.js. */
downloadSomethingSpecifc(searchCriteria: SearchCriteria): void {
download(this.url, searchCriteria)
.subscribe(
response => {
let file = response.blob();
console.log(file.size + " bytes file downloaded. File type: ", file.type);
saveAs(file, 'myCSV_Report.csv');
},
error => { /* handle errors */ }
);
}
Ответ 4
Вот самый простой способ загрузить файл из API, который мне удалось придумать.
import { Injectable } from '@angular/core';
import { Http, ResponseContentType } from "@angular/http";
import * as FileSaver from 'file-saver';
@Injectable()
export class FileDownloadService {
constructor(private http: Http) { }
downloadFile(api: string, fileName: string) {
this.http.get(api, { responseType: 'blob' })
.subscribe((file: Blob) => {
FileSaver.saveAs(file, fileName);
});
}
}
Вызовите метод downloadFile(api,fileName)
из класса вашего компонента.
Чтобы получить FileSaver, выполните следующие команды в своем терминале
npm install file-saver --save
npm install @types/file-saver --save
Ответ 5
Привет, вот пример рабочий. Он также подходит для PDF!
application/octet-stream - общий тип.
Контроллер:
public FileResult exportExcelTest()
{
var contentType = "application/octet-stream";
HttpContext.Response.ContentType = contentType;
RealisationsReportExcell reportExcell = new RealisationsReportExcell();
byte[] filedata = reportExcell.RunSample1();
FileContentResult result = new FileContentResult(filedata, contentType)
{
FileDownloadName = "report.xlsx"
};
return result;
}
Angular2:
Сервис xhr:
import { Injectable } from '@angular/core';
import { BrowserXhr } from '@angular/http';
@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {
super();
}
public build(): any {
let xhr = super.build();
xhr.responseType = "blob";
return <any>(xhr);
}
}
Установить файлы-хранители npm-пакетов "файл-хранитель": "^ 1.3.3", "@types/file-saver": "0.0.0" и включить в файл vendor.ts "файл-заставка"; /p >
Загрузка компонента btn.
import { Component, OnInit, Input } from "@angular/core";
import { Http, ResponseContentType } from '@angular/http';
import { CustomBrowserXhr } from '../services/customBrowserXhr.service';
import * as FileSaver from 'file-saver';
@Component({
selector: 'download-btn',
template: '<button type="button" (click)="downloadFile()">Download</button>',
providers: [
{ provide: CustomBrowserXhr, useClass: CustomBrowserXhr }
]
})
export class DownloadComponent {
@Input() api: string;
constructor(private http: Http) {
}
public downloadFile() {
return this.http.get(this.api, { responseType: ResponseContentType.Blob })
.subscribe(
(res: any) =>
{
let blob = res.blob();
let filename = 'report.xlsx';
FileSaver.saveAs(blob, filename);
}
);
}
}
Используя
<download-btn api="api/realisations/realisationsExcel"></download-btn>
Ответ 6
Чтобы заставить Filesaver работать в Angular 5: Установите
npm install file-saver --save
npm install @types/file-saver --save
В вашем компоненте используйте import * as FileSaver from "file-saver";
и использовать FileSaver. по умолчанию, а не FileSaver. Сохранить как
.subscribe(data => {
const blob = data.data;
const filename = "filename.txt";
FileSaver.default(blob, filename);
Ответ 7
Вот код, который работает для загрузки подписи API в IE и chrome/safari. Здесь переменная ответа является ответом API.
Примечание. HTTP-вызов клиента должен поддерживать ответ BLOB-объекта.
let blob = new Blob([response], {type: 'application/pdf'});
let fileUrl = window.URL.createObjectURL(blob);
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileUrl.split(':')[1] + '.pdf');
} else {
window.open(fileUrl);
}
Ответ 8
Рабочее решение с С# Web API, загружающее PDF в виде байтового массива:
С# загружает PDF как байтовый массив и преобразует в кодированную строку Base64
public HttpResponseMessage GetPdf(Guid id)
{
byte[] file = GetFile(id);
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StringContent("data:application/pdf;base64," + Convert.ToBase64String(file));
return result;
}
Угловой сервис получает PDF
getPdf(): Observable<string> {
return this.http.get(webApiRequest).pipe(
map(response => {
var anonymous = <any>response;
return anonymous._body;
})
);
}
Компонентное представление внедряет PDF через привязку к ответу сервиса
Переменная pdfSource
ниже - это возвращаемое значение из сервиса.
<embed [src]="sanitizer.bypassSecurityTrustResourceUrl(pdfSource)" type="application/pdf" width="100%" height="300px" />
См. Документацию Angular DomSanitizer для получения дополнительной информации.
Ответ 9
http
.post(url, data, {
responseType: "blob",
observe: "response"
})
.pipe(
map(response => {
saveAs(response.body, "fileName.pdf");
})
);