Servir un archivo para descargar en Angular
-
Servir un archivo
.txt
para descargar usando Angular -
Servir archivo para descargar con
ngx-filesaver
-
Servir un archivo
.jpg
para descargar usando Angular - Conclusión
La felicidad ignorante es cuando buscas descargar un archivo, inicias sesión en un sitio, buscas el archivo, lo encuentras, haces clic en descargar y terminas sin conocer las tareas y los esfuerzos detrás de servir esos archivos para descargar en ese sitio. Para los desarrolladores web, servir un archivo para descargar puede ser una tarea tal que se encontrará envidiando a estos usuarios inocentes.
Pero, ¿cómo servimos archivos para descargar usando el marco Angular? Consideremos algunos enfoques.
Servir un archivo .txt
para descargar usando Angular
El primer enfoque aquí es convertir un texto en formato .txt
y servirlo para su descarga. Para servir archivos para descargar, necesitamos utilizar blob, que es un almacenamiento virtual para entregar documentos directamente a un navegador.
Lo primero que debe hacer es comenzar un nuevo proyecto Angular. Luego, dentro del archivo app.component.html
, escribiremos estos códigos:
Fragmento de código- app.component.html
:
<a [href]="fileUrl" download="file.txt">DownloadFile</a>
Esto crea la estructura de la página web y el enlace en el que se hará clic para descargar.
Luego necesitamos crear una función y un comportamiento para la aplicación. Lo haremos dentro del archivo app.component.ts
, como se muestra a continuación:
Fragmento de código- app.component.ts
:
import { Component } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'done';
fileUrl;
constructor(private sanitizer: DomSanitizer) { }
ngOnInit() {
const data = 'some text';
const blob = new Blob([data], { type: 'application/octet-stream' });
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
}
}
Comenzamos definiendo los datos que deseamos guardar; es un texto, some text
. Luego definimos la función Blob
y declaramos los datos que queremos guardar dentro de la función.
Usaremos el módulo DomSanitizer
para permitir que el archivo que servimos para descargar pase la prueba de seguridad de Angular. Luego vamos al archivo app.module.ts
para importar los módulos que hemos usado para crear la app.
Fragmento de código- app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Producción:
Servir archivo para descargar con ngx-filesaver
Veamos ahora cómo podemos servir un archivo para descargar usando la dependencia ngx
en Angular. El ngx-filesaver
es un paquete seguro y popular que ofrece una forma sencilla de servir archivos para descargar.
El ngx-filesaver
se puede instalar escribiendo npm i ngx-filesaver
en el directorio de la carpeta del proyecto recién creado.
Para continuar, navegaremos hasta el archivo app.component.html
y escribiremos estos códigos para crear la estructura de la aplicación web.
Fragmento de código- app.component.html
:
<h1></h1>
<textarea [(ngModel)]="text"></textarea>
<button type="button" (click)="onDown()">Save Text</button>
Aquí, hemos creado un botón con el detector de eventos onDown
para que cuando se haga clic en el botón, se descargue el archivo que servimos.
Ahora debemos hacer el trabajo real dentro del archivo app.component.ts
, como se muestra a continuación:
Fragmento de código- app.component.ts
:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FileSaverService } from 'ngx-filesaver';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'loginfive';
public text = 'hello';
constructor(
private _httpClient: HttpClient,
private _FileSaverService: FileSaverService
) {}
ngOnInit() {}
onDown() {
const fileName = `save.txt`;
const fileType = this._FileSaverService.genType(fileName);
const txtBlob = new Blob([this.text], { type: fileType });
this._FileSaverService.save(txtBlob, fileName);
}
}
Observará que en el archivo app.component.ts
, indicamos el detector de eventos onDown
dentro de la etiqueta button
. Aquí, definimos las funciones para ello.
Primero, declaramos los datos que se servirán para su descarga; es un archivo de texto. Luego utilizamos blob para servir el archivo para su descarga en el navegador.
La última parte de este enfoque es importar los módulos que usamos para desarrollar la aplicación web.
Fragmento de código- app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { FileSaverModule } from 'ngx-filesaver';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule,
FormsModule,
HttpClientModule,
FileSaverModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Producción:
Servir un archivo .jpg
para descargar usando Angular
Hasta ahora, hemos estado analizando cómo publicar archivos .txt
para su descarga. Ahora veamos cómo podemos servir una imagen para descargar.
Obtendremos la imagen de una URL y la descarga mostrará una barra de progreso cortesía de Angular Material.
Primero debemos crear un nuevo proyecto Angular e instalar el paquete Angular Material, navegar a la carpeta del proyecto y escribir esto: ng add @*angular/material
.
Después de una instalación exitosa, pasaremos al archivo app.component.html
para crear la estructura de la página para incluir la diapositiva de progreso de la descarga.
Fragmento de código- app.component.html
:
<mat-card>
<div class="file">
<p>{{ slides.name }}</p>
<button (click)="download(slides)" mat-raised-button color="primary">Download</button>
</div>
<mat-progress-bar *ngIf="download$ | async as download"
[mode]="download.state == 'PENDING' ? 'buffer' : 'determinate'"
[value]="download.progress">
</mat-progress-bar>
</mat-card>
Necesitamos crear un componente donde trabajaremos en códigos para observar el progreso de la descarga, activando la diapositiva de material. Crearemos un archivo, lo llamaremos download.ts
, y trabajaremos estos códigos dentro.
Fragmento de código- download.ts
:
import {
HttpEvent,
HttpEventType,
HttpProgressEvent,
HttpResponse
} from "@angular/common/http";
import { Observable } from "rxjs";
import { distinctUntilChanged, scan, map, tap } from "rxjs/operators";
function isHttpResponse<T>(event: HttpEvent<T>): event is HttpResponse<T> {
return event.type === HttpEventType.Response;
}
function isHttpProgressEvent(
event: HttpEvent<unknown>
): event is HttpProgressEvent {
return (
event.type === HttpEventType.DownloadProgress ||
event.type === HttpEventType.UploadProgress
);
}
export interface Download {
content: Blob | any;
progress: number;
state: "PENDING" | "IN_PROGRESS" | "DONE";
}
export function download(
saver?: (b: Blob) => void
): (source: Observable<HttpEvent<Blob>>) => Observable<Download> {
return (source: Observable<HttpEvent<Blob>>) =>
source.pipe(
scan(
(download: Download, event): Download => {
if (isHttpProgressEvent(event)) {
return {
progress: event.total
? Math.round((100 * event.loaded) / event.total)
: download.progress,
state: "IN_PROGRESS",
content: null
};
}
if (isHttpResponse(event)) {
if (saver) {
saver(event.body);
}
return {
progress: 100,
state: "DONE",
content: event.body
};
}
return download;
},
{ state: "PENDING", progress: 0, content: null }
),
distinctUntilChanged((a, b) => a.state === b.state
&& a.progress === b.progress
&& a.content === b.content
)
);
}
Dado que necesitamos observar el progreso de descarga del archivo que servimos para descargar, usamos la función Observable
para monitorear el progreso de descarga y luego traducirlo al control deslizante de material. Lo siguiente es crear un archivo de servicio que manejará la obtención del archivo desde la URL y luego lo mostrará con la diapositiva de progreso.
Vamos a crear un archivo de servicio, asígnele el nombre download.service.ts
y escriba estos códigos:
Fragmento de código- download.service.ts
:
import { Injectable, Inject } from '@angular/core'
import { HttpClient, HttpRequest } from '@angular/common/http'
import { download, Download } from './download'
import { map } from 'rxjs/operators'
import { Observable } from 'rxjs'
import { SAVER, Saver } from './saver.provider'
@Injectable({providedIn: 'root'})
export class DownloadService {
constructor(
private http: HttpClient,
@Inject(SAVER) private save: Saver
) {
}
download(url: string, filename?: string): Observable<Download> {
return this.http.get(url, {
reportProgress: true,
observe: 'events',
responseType: 'blob'
}).pipe(download(blob => this.save(blob, filename)))
}
blob(url: string, filename?: string): Observable<Blob> {
return this.http.get(url, {
responseType: 'blob'
})
}
}
Para proporcionar estos servicios a nuestra aplicación, necesitamos un proveedor, creamos un nuevo archivo, lo llamamos saver.provider.ts
y le ponemos los códigos.
Fragmento de código- saver.provider.ts
:
import {InjectionToken} from '@angular/core'
import { saveAs } from 'file-saver';
export type Saver = (blob: Blob, filename?: string) => void
export const SAVER = new InjectionToken<Saver>('saver')
export function getSaver(): Saver {
return saveAs;
}
Lo siguiente es obtener la URL de la imagen que queremos servir para descargar. Lo haremos dentro del archivo app.component.ts
y escribiremos estos códigos.
Fragmento de código- app.component.ts
:
import { Component, Inject } from '@angular/core';
import { DownloadService } from './download.service'
import { Download } from './download'
import { Observable } from 'rxjs'
import { tap } from 'rxjs/operators'
import { DOCUMENT } from '@angular/common'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'done';
slides =
{name: 'Click here:', url: 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_960_720.jpg'}
download$: Observable<Download>
constructor(private downloads: DownloadService,
@Inject(DOCUMENT) private document: Document) {}
download({name, url}: {name: string, url: string}) {
this.download$ = this.downloads.download(url, name)
}
}
Necesitamos toneladas de módulos para que la aplicación funcione. Necesitamos importar estos módulos dentro del archivo app.module.ts
.
Fragmento de código- app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'
import {MatButtonModule} from '@angular/material/button';
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatCardModule} from '@angular/material/card';
import { BrowserAnimationsModule} from '@angular/platform-browser/animations'
import { AppComponent } from './app.component';
import { SAVER, getSaver } from './saver.provider'
@NgModule({
imports: [
BrowserModule,
FormsModule,
BrowserAnimationsModule,
HttpClientModule,
MatButtonModule,
MatProgressBarModule,
MatCardModule
],
providers: [{provide: SAVER, useFactory: getSaver}],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Luego necesitamos embellecer la página con algunos estilos. Agregaremos este pequeño fragmento dentro del archivo app.component.css
.
Fragmento de código- app.component.css
:
mat-card {
display: flex;
flex-direction: column;
}
.file {
display: flex;
justify-content: space-between;
align-items: center;
}
Producción:
Conclusión
Dependiendo del archivo que queramos servir para descargar, hay varios enfoques que podemos adoptar para implementar esto dentro de nuestra aplicación Angular, usando Angular blob
para guardar los archivos.
Fisayo is a tech expert and enthusiast who loves to solve problems, seek new challenges and aim to spread the knowledge of what she has learned across the globe.
LinkedIn