- Dokumente in Angular: Upload & Download - 29. März 2019
- Externe Daten: Zentrales Statemanagement mit Redux - 28. März 2019
- Routenbasierte Daten mit Resolver laden - 27. März 2019
Upload & Download
In diesem letzten Teil werfen wir einen Blick auf die Dateiupload und Download. Weitere Teile zu der Blogpost Reihe “Externe Daten in Angular” findest du hier: Externe Daten in Angular – Ein Überblick zur Blogpost-Reihe
Upload
Zu den Dateien gehören auch mehr als nur die binären Daten, sondern auch beschreibende Daten wie Dateiname. Das Übertragen des Dateninhalts reicht meistens nicht aus.
Um weitere Informationen zu übertragen, nutzen wir eine FormData
. Neben der Datei übergeben wir auch den Dateinamen. Für die Übertragung der Daten nutzen wir das HttpClientModule
von Angular. Der/die Benutzer:in soll informiert werden, wie der Fortschritt des Dateiuploads ist. Dazu werden wir neben jeden Dateiupload einen Fortschrittsbalken darstellen. Um die Status der Übertragung zu nutzen, müssen wir den Parameter {reportProgress: true}
angeben. Die upload
-Methode liefert einen Observable<number>
zurück. Die ganzzahlige zurückgelieferte Zahl, stellt den Fortschritt in Prozent da. 1 entspricht 1 %, 42 entspricht 42 % und fertig ist der Upload, wenn 100 zurückgeliefert wird.
import {Injectable} from '@angular/core';
import {HttpClient, HttpEventType, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class UploadService {
readonly url = 'http://localhost:8000/upload';
constructor(private http: HttpClient) {
}
public upload(file: File): Observable {
const formData: FormData = new FormData();
formData.append('file', file, file.name);
const req = new HttpRequest('POST', this.url, formData, {
reportProgress: true
});
return this.http.request(req).pipe(map(event => {
if (event.type === HttpEventType.UploadProgress) {
return Math.round(100 * event.loaded / event.total);
} else if (event instanceof HttpResponse) {
return 100;
}
}));
}
}
Für die Interaktion mit den Benutzer:innen steht das HTML Element <input type="file" multiple />
.
Da das Standardelement optisch nicht zu unserer Anwendung passt, haben wir das Element versteckt. Der/die Benutzer:in klickt auf einen anderen Button. Programmatisch wird anschließend das versteckte Standardelement geklickt und der/die Nutzer:in kann die Dateien für den Upload aussuchen.
Auf dem Change-Event
(change)="onFilesAdded()"
wird der Upload ausgelöst. Da ggf. mehrere Dateien hochgeladen werden, wird in einer for
Schleife für jedes einzelne Dokument der Upload gestartet.
Durch [ngStyle]="{width: (uploadFile.status | async) + '%'}"
wird im Template die Fortschrittsanzeige realisiert.
Der gesamte Programmcode ist unter https://github.com/dornsebastian/angular-file-updownload (Angular Frontend)
und https://github.com/dornsebastian/express-file-updownload (NodeJS Backend) zu finden.
@Component({
selector: 'app-upload',
templateUrl: './upload.component.html',
styleUrls: ['./upload.component.css']
})
export class UploadComponent implements OnInit {
@ViewChild('file') file;
public files: UploadFileEntity[] = [];
constructor(private uploadService: UploadService) {
}
ngOnInit() {
}
onFilesAdded() {
const files: { [key: string]: File } = this.file.nativeElement.files;
for (const key in files) {
if (!isNaN(Number(key))) {
const file = files[key];
const status = this.uploadService.upload(file);
this.files.unshift(new UploadFileEntity(file, status));
}
}
}
addFiles() {
this.file.nativeElement.click();
}
}
Download
Die Herausforderung bei Downloads ist eine andere: Die Datei soll nicht im Browser geöffnet werden. Die Datei soll in den Downloads-Ordner oder in einem anderen Ordner gespeichert werden.
Für die technische Umsetzung nutzen wir in diese Codebeispiel die Bibliothek file-saver
. Mit der Library können binäre Daten auf Benutzerwunsch in den Downloads gespeichert werden.
Neben den binären Daten (Blob) muss noch der gewünschte Dateiname angegeben werden.
export const saveFile = (blobContent: Blob, fileName: string) => {
const blob = new Blob([blobContent], {type: 'application/octet-stream'});
saveAs(blob, fileName);
};
Die Methode downloadFile
lädt die Datei von einer URL herunter und
speichert diese dann lokal mit der Bibliothek file-saver
.
downloadFile() {
const filename = 'photo.jpg';
// Process the file downloaded
this.http.get(this.url, {responseType: 'blob'}).subscribe(blob => {
saveFile(blob, filename);
});
}
}
Dateigröße
Abschließend noch ein paar Worte zu den Dateigrößen. Der Beispielcode behandelt die Dokumente zu hoch- und herunterladen am Stück. Dies ist für kleine Dateien geeignet – jedoch nicht für große Dateien. Für große Dateien sollten die Dateien als Stream ausgetauscht werden.
Der angesprochene Programmcode ist unter GitHub zu finden:
- Angular Frontend: https://github.com/dornsebastian/angular-file-updownload
- NodeJS Backend: https://github.com/dornsebastian/express-file-updownload