Angular: memory-leak after concurrent downloads?

Viewed 406

I'm developing an Angular app and I created a download page like this:

enter image description here

If I have a look at Chrome task manager I see that the process related to my app keeps about 130 MB of memory.

Then, users can launch simultaneous downloads clicking on every download button (the total files size is 266 MB):

enter image description here

Now, if I check again the task manager I see that the memory usage growns and when all downloads are finished its peak is about 650 MB. I noticed a weird thing: the final usage of the memory seems to be the total of the following formula:

initial memory usage + 2 * total files size = final memory usage

130 MB + 2 * 266 MB = 130 MB + 532 MB = 662 MB = ~650 MB.

If i try to use the Chrome garbage collector it cuts off about 30 MB, but I still have the app that has 620 MB as memory usage. So, how can I fix this behaviour? Can you see anything wrong with my code? (I have tried to use unsubscribe on Component's ngOnDestroy, but it didn't work)

Here is the function that is called when I click on the download button of a file (from a download page):

getFile(file: any): void {
    this.fileDownloadService.downloadFile(file).subscribe(
        (fileinfo: SispFile) => {
            file.downloading = true;
        },
        (err: any) => {
            file.downloading = false;
            console.log('errore', err);
        },
        () => {
            file.downloading = false;
            console.log('completated');
        });
}

Here is the downloadFile function in the FileDownloadService, called by getFile:

downloadFile(file: SispFile): Observable<any> {
    let conf: any = {};
    conf.totalChunks = Math.max(Math.ceil(file.size / this.chunkSizeDownload), 1);
    conf.mime = file.extension;
    conf.currentChunk = 0;
    conf.byteDownloaded = 0;
    conf.chunkSizeDownload = this.chunkSizeDownload;
    conf.chunkBlobs = [];
    conf.finalBlob = null;

    file.progress = 0;

    return new Observable(observer => {
        observer.next(file);
        this.addChunk(file, observer, conf);
    })
}

addChunk(file: SispFile, observer: any, conf: any) {
    if (conf.currentChunk == conf.totalChunks) {
        observer.complete();
        conf.finalBlob = new Blob(conf.chunkBlobs, {type: conf.mime});

        let fileURL = URL.createObjectURL(conf.finalBlob);
        let a = document.createElement("a");
        a.href = fileURL;
        a.download = file.name;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(fileURL);
    } else {
        this.docService.downloadFile(file, conf.chunkSizeDownload, conf.byteDownloaded)
            .then(response => {
                let typedArray = this.createBlob(response['_body'], conf.mime);
                conf.chunkBlobs[conf.currentChunk] = typedArray;
                conf.currentChunk++;
                conf.byteDownloaded = conf.currentChunk * conf.chunkSizeDownload;
                if (conf.Downloaded + this.chunkSizeDownload > file.size) {
                    conf.chunkSizeDownload = file.size - conf.Downloaded + 1;
                }

                let progress = Math.round((conf.currentChunk * 100) / conf.totalChunks);
                file.progress = progress;
                observer.next(file);
                this.addChunk(file, observer, conf);
            })
            .catch((error: ErrorMessage) => {
                observer.error(error);
                console.log('Errore server: ' + error);
            });
    }
}

And here is the final call to back-end endpoint in the DocService:

downloadFile(file: SispFile, length: number, offset: number) {
    let url = Util.format(this.downloadFileUrl, {id: file.id});
    let body: any = { "downloadMode": "PAYLOAD", "fileId": file.id, "length": length, "offset": offset };
    return this.http.post(url, body)
        .toPromise()
        .then(response => {
            return response;
        })
        .catch(this.handleError);
}
1 Answers
Related