J'ai une application WebApi/MVC pour laquelle je développe un client angular2 (pour remplacer MVC). J'ai des difficultés à comprendre comment Angular enregistre un fichier.
La demande est ok (fonctionne bien avec MVC, et nous pouvons enregistrer les données reçues), mais je ne sais pas comment enregistrer les données téléchargées (je suis principalement dans la même logique que dans cet article ). Je suis sûr que c'est bêtement simple, mais jusqu'ici je ne le comprends tout simplement pas.
Le code de la fonction composant est ci-dessous. J'ai essayé différentes alternatives, la manière de blob devrait être la voie à suivre pour autant que je sache, mais il n'y a pas de fonction createObjectURL
dans URL
. Je ne trouve même pas la définition de URL
dans la fenêtre, mais apparemment, elle existe. Si j'utilise le module FileSaver.js
j'obtiens la même erreur. Donc, je suppose que c'est quelque chose qui a changé récemment ou qui n'est pas encore implémenté. Comment puis-je déclencher l'enregistrement du fichier en A2?
downloadfile(type: string){
let thefile = {};
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
let url = window.URL.createObjectURL(thefile);
window.open(url);
}
Par souci d'exhaustivité, le service qui récupère les données se trouve en dessous, mais il ne fait qu'émettre la demande et transmettre les données sans mappage s'il réussit:
downloadfile(runname: string, type: string){
return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
.catch(this.logAndPassOn);
}
Le problème est que l'observable s'exécute dans un autre contexte. Ainsi, lorsque vous essayez de créer l'URL var, vous avez un objet vide et non le blob que vous souhaitez.
Une des nombreuses façons de résoudre ce problème est la suivante:
this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
error => console.log('Error downloading the file.'),
() => console.info('OK');
Lorsque la demande est prête, la fonction "downloadFile" sera définie comme suit:
downloadFile(data: Response) {
const blob = new Blob([data], { type: 'text/csv' });
const url= window.URL.createObjectURL(blob);
window.open(url);
}
le blob a été créé parfaitement et donc l'URL var, si ne s'ouvre pas, vérifiez que vous avez déjà importé 'rxjs/Rx';
import 'rxjs/Rx' ;
J'espère que cela pourra vous aider.
Essayez this !
1 - Installer les dépendances pour afficher le fichier de sauvegarde/ouvrir un fichier contextuel
npm install file-saver --save
npm install @types/file-saver --save
2- Créez un service avec cette fonction pour recevoir les données
downloadFile(id): Observable<Blob> {
let options = new RequestOptions({responseType: ResponseContentType.Blob });
return this.http.get(this._baseUrl + '/' + id, options)
.map(res => res.blob())
.catch(this.handleError)
}
3- Dans le composant, analysez le blob avec 'file-saver'
import {saveAs as importedSaveAs} from "file-saver";
this.myService.downloadFile(this.id).subscribe(blob => {
importedSaveAs(blob, this.fileName);
}
)
Cela fonctionne pour moi!
Si vous n'avez pas besoin d'ajouter des en-têtes dans la demande, pour télécharger un fichier dans Angular2, vous pouvez effectuer une simple:
window.location.href='http://example.com/myuri/report?param=x';
dans votre composant.
Comme mentionné par Alejandro Corredor c'est une simple erreur de champ d'application. La subscribe
est exécutée de manière asynchrone et la open
doit être placée dans ce contexte pour que le chargement des données se termine lorsque nous déclenchons le téléchargement.
Cela dit, il y a deux façons de le faire. Comme le recommande la documentation, le service s'occupe de récupérer et de mapper les données:
//On the service:
downloadfile(runname: string, type: string){
var headers = new Headers();
headers.append('responseType', 'arraybuffer');
return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
.map(res => new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
.catch(this.logAndPassOn);
}
Ensuite, nous nous abonnons simplement au composant et traitons les données mappées. Il y a deux possibilités. Le premier, comme suggéré dans le message d'origine, mais nécessite une petite correction, comme l'a noté Alejandro:
//On the component
downloadfile(type: string){
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(data => window.open(window.URL.createObjectURL(data)),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
}
_ {La deuxième} serait d’utiliser FileReader. La logique est la même, mais nous pouvons explicitement attendre que FileReader charge les données, en évitant l'imbrication et en résolvant le problème asynchrone.
//On the component using FileReader
downloadfile(type: string){
var reader = new FileReader();
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(res => reader.readAsDataURL(res),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
reader.onloadend = function (e) {
window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
}
}
Note: J'essaie de télécharger un fichier Excel et même si le téléchargement est déclenché (donc cela répond à la question), le fichier est corrompu. Voir la réponse à ce message for en évitant le fichier corrompu.
C'est pour les gens qui cherchent comment faire avec HttpClient et File-Saver
npm install file-saver --save
npm install @ types/fichier-économiseur --save
Classe de service API:
export() {
return this.http.get(this.download_endpoint,
{responseType: 'blob'});
}
Composant:
import { saveAs } from 'file-saver';
exportPdf() {
this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}
Téléchargez la solution * .Zip pour angular 2.4.x: vous devez importer ResponseContentType à partir de '@ angular/http' et changer responseType en ResponseContentType.ArrayBuffer (par défaut, ResponseContentType.Json)
getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
let headers = this.setHeaders({
'Content-Type': 'application/Zip',
'Accept': 'application/Zip'
});
return this.http.get(`${environment.apiUrl}${path}`, {
headers: headers,
search: params,
responseType: ResponseContentType.ArrayBuffer //magic
})
.catch(this.formatErrors)
.map((res:Response) => res['_body']);
}
Que dis-tu de ça?
this.http.get(targetUrl,{responseType:ResponseContentType.Blob})
.catch((err)=>{return [do yourself]})
.subscribe((res:Response)=>{
var a = document.createElement("a");
a.href = URL.createObjectURL(res.blob());
a.download = fileName;
// start download
a.click();
})
Je pourrais faire avec ça.
pas besoin de paquet supplémentaire.
Télécharger un fichier via ajax est toujours un processus difficile et, à mon avis, il est préférable de laisser le serveur et le navigateur effectuer ce travail de négociation du type de contenu.
Je pense que c'est mieux d'avoir
<a href="api/sample/download"></a>
pour le faire. Cela ne nécessite même pas l'ouverture de nouvelles fenêtres et ce genre de choses.
Le contrôleur MVC, comme dans votre exemple, peut ressembler à celui ci-dessous:
[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
// ...
return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}
Pour les nouvelles versions angulaires:
npm install file-saver --save
npm install @types/file-saver --save
import {saveAs} from 'file-saver/FileSaver';
this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
.subscribe(blob => {
saveAs(blob, 'download.pdf');
});
Je partage la solution qui m'a aidé (toute amélioration est grandement appréciée)
Sur votre service 'pservice':
getMyFileFromBackend(typeName: string): Observable<any>{
let param = new URLSearchParams();
param.set('type', typeName);
// setting 'responseType: 2' tells angular that you are loading an arraybuffer
return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
.map(res => res.text())
.catch((error:any) => Observable.throw(error || 'Server error'));
}
Composant partie:
downloadfile(type: string){
this.pservice.getMyFileFromBackend(typename).subscribe(
res => this.extractData(res),
(error:any) => Observable.throw(error || 'Server error')
);
}
extractData(res: string){
// transforme response to blob
let myBlob: Blob = new Blob([res], {type: 'application/vnd.oasis.opendocument.spreadsheet'}); // replace the type by whatever type is your response
var fileURL = URL.createObjectURL(myBlob);
// Cross your fingers at this point and pray whatever you're used to pray
window.open(fileURL);
}
Sur la partie composant, vous appelez le service sans vous abonner à une réponse. Pour vous procurer une liste complète des types de mime openOffice, voir: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html
Pour ceux qui utilisent Redux Pattern
J'ai ajouté dans l'économiseur de fichier en tant que @Hector Cuevas nommé dans sa réponse. En utilisant Angular2 v. 2.3.1, je n’avais pas besoin d’ajouter dans @ types/fichier-économiseur.
L'exemple suivant consiste à télécharger un journal au format PDF.
Les actions du journal
public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action {
return {
type: JournalActions.DOWNLOAD_JOURNALS,
payload: { referenceId: referenceId }
};
}
public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action {
return {
type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
payload: { blob: blob }
};
}
Les effets de journal
@Effect() download$ = this.actions$
.ofType(JournalActions.DOWNLOAD_JOURNALS)
.switchMap(({payload}) =>
this._journalApiService.downloadJournal(payload.referenceId)
.map((blob) => this._actions.downloadJournalsSuccess(blob))
.catch((err) => handleError(err, this._actions.downloadJournalsFail(err)))
);
@Effect() downloadJournalSuccess$ = this.actions$
.ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
.map(({payload}) => saveBlobAs(payload.blob, 'journal.pdf'))
Le journal de service
public downloadJournal(referenceId: string): Observable<any> {
const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`;
return this._http.getBlob(url);
}
Le service HTTP
public getBlob = (url: string): Observable<any> => {
return this.request({
method: RequestMethod.Get,
url: url,
responseType: ResponseContentType.Blob
});
};
Le réducteur de journal Bien que cela ne définisse que les états corrects utilisés dans notre application, je souhaitais quand même l'ajouter pour montrer le motif complet.
case JournalActions.DOWNLOAD_JOURNALS: {
return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] });
}
case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] });
}
J'espère que ceci est utile.
Pour télécharger et afficherPDFfichiers, un extrait de code très similaire se présente comme suit:
private downloadFile(data: Response): void {
let blob = new Blob([data.blob()], { type: "application/pdf" });
let url = window.URL.createObjectURL(blob);
window.open(url);
}
public showFile(fileEndpointPath: string): void {
let reqOpt: RequestOptions = this.getAcmOptions(); // getAcmOptions is our helper method. Change this line according to request headers you need.
reqOpt.responseType = ResponseContentType.Blob;
this.http
.get(fileEndpointPath, reqOpt)
.subscribe(
data => this.downloadFile(data),
error => alert("Error downloading file!"),
() => console.log("OK!")
);
}
J'utilise Angular 4 avec l'objet 4.3 httpClient. J'ai modifié une réponse trouvée dans le blog technique de Js, qui crée un objet de lien, l'utilise pour effectuer le téléchargement, puis le détruit.
Client:
doDownload(id: number, contentType: string) {
return this.http
.get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' })
}
downloadFile(id: number, contentType: string, filename:string) {
return this.doDownload(id, contentType).subscribe(
res => {
var url = window.URL.createObjectURL(res);
var a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
a.remove(); // remove the element
}, error => {
console.log('download error:', JSON.stringify(error));
}, () => {
console.log('Completed file download.')
});
}
La valeur de this.downloadUrl a été définie précédemment pour pointer sur l'api. J'utilise ceci pour télécharger des pièces jointes, je connais donc l'id, le contentType et le nom du fichier:
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public FileContentResult GetAttachment(Int32 attachmentID)
{
Attachment AT = filerep.GetAttachment(attachmentID);
if (AT != null)
{
return new FileContentResult(AT.FileBytes, AT.ContentType);
}
else
{
return null;
}
}
La classe de pièce jointe ressemble à ceci:
public class Attachment
{
public Int32 AttachmentID { get; set; }
public string FileName { get; set; }
public byte[] FileBytes { get; set; }
public string ContentType { get; set; }
}
Le référentiel filerep renvoie le fichier à partir de la base de données.
J'espère que ça aide quelqu'un :)
Effectuez la mise à jour de la réponse d'Hector à l'aide de File-Saver et de HttpClient à l'étape 2:
public downloadFile(file: File): Observable<Blob> {
return this.http.get(file.fullPath, {responseType: 'blob'})
}
Voici quelque chose que j'ai fait dans mon cas -
// service method
downloadFiles(vendorName, fileName) {
return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: 'arraybuffer' }).map((res: ArrayBuffer) => { return res; })
.catch((error: any) => _throw('Server error: ' + error));
}
// a controller function which actually downloads the file
saveData(data, fileName) {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
let blob = new Blob([data], { type: "octet/stream" }),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
// a controller function to be called on requesting a download
downloadFiles() {
this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
() => console.info("OK"));
}
La solution est référencée de - here
J'ai eu une solution pour télécharger de angular 2 sans être corrompu, En utilisant spring mvc et angular 2
1er- mon type de retour est: - ResponseEntity de Java end. Ici, j'envoie un octet []. Le tableau a le type de retour du contrôleur.
2ème pour inclure l'économiseur de fichiers dans votre espace de travail dans la page d'index en tant que:
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
Le troisième composant enregistre ce code:
import {ResponseContentType} from '@angular.core';
let headers = new Headers({ 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' });
let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
this.http
.post('/project/test/export',
somevalue,options)
.subscribe(data => {
var mediaType = 'application/vnd.ms-Excel';
let blob: Blob = data.blob();
window['saveAs'](blob, 'sample.xls');
});
Cela vous donnera le format de fichier xls. Si vous souhaitez utiliser d'autres formats, modifiez le type de média et le nom du fichier avec l'extension appropriée.
Ce sera mieux si vous essayez d'appeler la nouvelle méthode à l'intérieur de vous subscribe
this._reportService.getReport()
.subscribe((data: any) => {
this.downloadFile(data);
},
(error: any) => onsole.log(error),
() => console.log('Complete')
);
Dans la fonction downloadFile(data)
, nous devons créer block, link, href and file name
downloadFile(data: any, type: number, name: string) {
const blob = new Blob([data], {type: 'text/csv'});
const dataURL = window.URL.createObjectURL(blob);
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob);
return;
}
const link = document.createElement('a');
link.href = dataURL;
link.download = 'export file.csv';
link.click();
setTimeout(() => {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(dataURL);
}, 100);
}
}
Vous pouvez également télécharger un fichier directement à partir de votre modèle où vous utilisez l'attribut download et à [attr.href]
vous pouvez fournir une valeur de propriété à partir du composant. Cette solution simple devrait fonctionner sur la plupart des navigateurs.
<a download [attr.href]="yourDownloadLink"></a>
Référence: https://www.w3schools.com/tags/att_a_download.asp
<a href="my_url" download="myfilename">Download file</a>
my_url devrait avoir la même origine, sinon il sera redirigé vers cet emplacement
Il suffit de mettre url
comme href
comme ci-dessous
<a href="my_url">Download File</a>
Je faisais face au même cas aujourd'hui, je devais télécharger un fichier pdf en pièce jointe (le fichier ne devrait pas être rendu dans le navigateur, mais téléchargé à la place). Pour y parvenir, j'ai découvert que je devais obtenir le fichier dans un Angular Blob
et, en même temps, ajouter un en-tête Content-Disposition
dans la réponse.
C'était le plus simple que j'ai pu obtenir (Angular 7):
À l'intérieur du service:
getFile(id: String): Observable<HttpResponse<Blob>> {
return this.http.get(`./file/${id}`, {responseType: 'blob', observe: 'response'});
}
Ensuite, lorsque je dois télécharger le fichier dans un composant, je peux simplement:
fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);
METTRE À JOUR:
Suppression des paramètres d'en-tête inutiles du service
let headers = new Headers({
'Content-Type': 'application/json',
'MyApp-Application': 'AppName',
'Accept': 'application/vnd.ms-Excel'
});
let options = new RequestOptions({
headers: headers,
responseType: ResponseContentType.Blob
});
this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
.subscribe(data => {
if (navigator.appVersion.toString().indexOf('.NET') > 0)
window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");
else {
var a = document.createElement("a");
a.href = URL.createObjectURL(data.blob());
a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
a.click();
}
this.ui_loader = false;
this.selectedexport = 0;
}, error => {
console.log(error.json());
this.ui_loader = false;
document.getElementById("exceptionerror").click();
});