Качалов Тимофей February 2016

Observable and xhr.upload.onprogress event

I have two services.

1) UploadService - send file throug ajax to server, and also get and store progress value (inside xhr.upload.onprogress event handler)

2) SomeOtherService worked with DOM, get progress value from first service, displayed it on Bootstrap progressbar.

Because, xhr.upload.onprogress is asynchronious - i use Observable in first service:

constructor () {
    this.progress$ = new Observable(observer => {
        this.progressObserver = observer
    }).share();
}

private makeFileRequest (url: string, params: string[], files: File[]): Promise<any> {
    return new Promise((resolve, reject) => {
        let formData: FormData = new FormData(),
            xhr: XMLHttpRequest = new XMLHttpRequest();

        for (let i = 0; i < files.length; i++) {
            formData.append("uploads[]", files[i], files[i].name);
        }

        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(JSON.parse(xhr.response));
                } else {
                    reject(xhr.response);
                }
            }
        };

        xhr.upload.onprogress = (event) => {
            this.progress = Math.round(event.loaded / event.total * 100);

            this.progressObserver.next(this.progress);
        };

        xhr.open('POST', url, true);
        xhr.send(formData);
    });
}

Insise second service, i subscribe on this observer:

this.fileUploadService.progress$.subscribe(progress => {
    this.uploadProgress = progress
});

this.fileUploadService.upload('/api/upload-file', [], this.file);

Now my problem:

That code does not work. In second service i get observed value only once, on 100%.

But if i insert (yes, with empty callback body)

setInterval(() =        

Answers


Thierry Templier February 2016

I don't have the complete code so it's difficult to give you a precise answer.

The fact you tell that "In second service i get observed value only once, on 100%." makes me think that it should be related to the use of promises. As a matter of fact, promises can be resolved or rejected once. There is no support of multiple events contrary to observables.

If you can post a plunkr with you code, I'll be able to debug it so I should provide you a more precise answer.

Edit

I created a plunrk (http://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=preview) to test your code and it seems that it works. By changing the promise into an observable, I get more progress hints.

makeFileRequest(url: string, params: string[], files: File[]): Observable> {
  return Observable.create(observer => {
    let formData: FormData = new FormData(),
        xhr: XMLHttpRequest = new XMLHttpRequest();

    for (let i = 0; i < files.length; i++) {
        formData.append("uploads[]", files[i], files[i].name);
    }

    xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                observer.next(JSON.parse(xhr.response));
                observer.complete();
            } else {
                observer.error(xhr.response);
            }
        }
    };

    xhr.upload.onprogress = (event) => {
        this.progress = Math.round(event.loaded / event.total * 100);

        this.progressObserver.next(this.progress);
    };

    xhr.open('POST', url, true);
    xhr.send(formData);
  });
}

Here is the traces I had:

app.component.ts:18 progress = 21
app.component.ts:18 progress = 44
app.component.ts:18 progress = 71
app.component.ts:18 progress = 100

With the promise, I only get two traces

Post Status

Asked in February 2016
Viewed 3,784 times
Voted 13
Answered 1 times

Search




Leave an answer