angular2 + 文件上传进度指示器
angular2 + file upload progress indicator
我有一个现有的表单上传组件,可以将文件上传到后端,这工作正常,但问题是我没有任何视觉指示器来指示有多少文件,即已上传到服务器的文件的百分比。我相信这是我的这个组件中缺少的非常需要的功能。目前 Please wait
源于 app-loader.service.ts(截图),
我可以修改 Please wait
并上传 % of file uploaded
吗?我在网上看到了很多使用进度条的答案,但我宁愿有一个对我有意义并适合我的具体情况的解决方案。
表格-upload.component.ts
import { Component, ViewChild, } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { Observer } from 'rxjs/Observer';
import { HttpClient} from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { FieldConfig } from '../../models/field-config.interface';
import { WebSocketService, DialogService } from '../../../../../../services/';
import { AppLoaderService } from '../../../../../../services/app-loader/app-loader.service';
import { Http } from '@angular/http';
import { MatSnackBar } from '@angular/material';
@Component({
selector: 'app-form-upload',
templateUrl: './form-upload.component.html',
styleUrls: ['../dynamic-field/dynamic-field.css', 'form-upload.component.css'],
})
export class FormUploadComponent {
@ViewChild('fileInput') fileInput;
config: FieldConfig;
group: FormGroup;
fieldShow: string;
public busy: Subscription[] = [];
public sub: Subscription;
public observer: Observer < any > ;
public jobId: Number;
public fileBrowser = true;
public apiEndPoint = '/_upload?auth_token=' + this.ws.token;
constructor(
protected ws: WebSocketService, protected http: Http, private loader: AppLoaderService,
private dialog:DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
upload(location = "/tmp/") {
if(this.config.updater && this.config.parent ){
this.config.updater(this, this.config.parent);
return;
}
this.loader.open();
const fileBrowser = this.fileInput.nativeElement;
if (fileBrowser.files && fileBrowser.files[0]) {
const formData: FormData = new FormData();
formData.append('data', JSON.stringify({
"method": "filesystem.put",
"params": [location + '/' + fileBrowser.files[0].name, { "mode": "493" }]
}));
formData.append('file', fileBrowser.files[0]);
this.http.post(this.apiEndPoint, formData).subscribe(
(data) => {
this.newMessage(location + '/' + fileBrowser.files[0].name);
this.loader.close();
this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
},
(error) => {
this.loader.close();
this.dialog.errorReport(error.status, error.statusText, error._body);
}
);
} else{
this.loader.close();
};
}
newMessage(message){
if(this.config.message){
this.config.message.newMessage(message);
}
}
}
表格-upload.component.html
<div id="{{config.name}}" class="dynamic-field form-input" [formGroup]="group" [ngClass]="fieldShow" [class.has-tooltip]="config.tooltip" *ngIf="!config.isHidden">
<div class="top">
<label>{{ config.placeholder | translate }}</label>
<tooltip *ngIf="config.tooltip" [message]="config.tooltip"></tooltip>
</div>
<div *ngIf="config.hideButton;else showButton">
<mat-card-content>
<input type="file" #fileInput accept="{{config.acceptedFiles}}" (change)="upload(config.fileLocation)" [formControlName]="config.name">
</mat-card-content>
</div>
<ng-template #showButton>
<mat-card-content>
<input type="file" #fileInput accept="{{config.acceptedFiles}}" [formControlName]="config.name">
</mat-card-content>
<mat-card-actions class="buttons">
<button mat-button type="button" (click)="upload(config.fileLocation)">Upload</button>
</mat-card-actions>
<mat-error *ngIf="config.hasErrors">{{config.errors}}</mat-error>
</ng-template>
</div>
app-loader.service.ts
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';
import { Observable } from 'rxjs/Rx';
import { AppLoaderComponent } from './app-loader.component';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../../translate-marker';
@Injectable()
export class AppLoaderService {
dialogRef: MatDialogRef<AppLoaderComponent>;
constructor(private dialog: MatDialog, private translate: TranslateService) { }
public open(title: string = T('Please wait')): Observable<boolean> {
this.translate.get(title).subscribe(t => {
this.dialogRef = this.dialog.open(AppLoaderComponent, {disableClose: true});
this.dialogRef.updateSize('200px', '200px');
this.dialogRef.componentInstance.title = t;
});
return this.dialogRef.afterClosed();
}
public close() {
this.dialogRef.close();
}
}
我在 https://malcoded.com/posts/angular-file-upload-component-with-express
查看 src/app/upload/upload.service.ts
的源代码后想出的解决方案
如果有人感兴趣,这里是差异。
diff --git a/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts b/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
index 68be9cdf5..2aec0dd86 100644
--- a/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
+++ b/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
@@ -2,13 +2,13 @@ import { Component, ViewChild, } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { Observer } from 'rxjs/Observer';
-import { HttpClient} from '@angular/common/http';
+import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { FieldConfig } from '../../models/field-config.interface';
-import { WebSocketService, DialogService } from '../../../../../../services/';
+import { WebSocketService } from '../../../../../../services/';
import { AppLoaderService } from '../../../../../../services/app-loader/app-loader.service';
-import { Http } from '@angular/http';
import { MatSnackBar } from '@angular/material';
+import { DialogService } from '../../../../../../services/';
@Component({
@@ -27,10 +27,10 @@ export class FormUploadComponent {
public jobId: Number;
public fileBrowser = true;
public apiEndPoint = '/_upload?auth_token=' + this.ws.token;
-
+
constructor(
- protected ws: WebSocketService, protected http: Http, private loader: AppLoaderService,
- private dialog:DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
+ protected ws: WebSocketService,protected http: HttpClient, private loader: AppLoaderService,
+ public dialog: DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
upload(location = "/tmp/") {
if(this.config.updater && this.config.parent ){
@@ -47,21 +47,29 @@ export class FormUploadComponent {
"params": [location + '/' + fileBrowser.files[0].name, { "mode": "493" }]
}));
formData.append('file', fileBrowser.files[0]);
+ const req = new HttpRequest('POST', this.apiEndPoint, formData, {
+ reportProgress: true
+ });
+ this.http.request(req).subscribe(event => {
+ if (event.type === HttpEventType.UploadProgress) {
+ const percentDone = Math.round(100 * event.loaded / event.total);
+ const upload_msg = `${percentDone}% Uploaded`
+ this.loader.dialogRef.componentInstance.title = upload_msg;
+
+ } else if (event instanceof HttpResponse) {
+ if(event.statusText==="OK") {
+ this.newMessage(location + '/' + fileBrowser.files[0].name);
+ this.loader.close();
+ this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
+ }
+ };
+ },(error)=> {
+ this.loader.close();
+ this.dialog.errorReport("Error",error.statusText, error.message);
- this.http.post(this.apiEndPoint, formData).subscribe(
- (data) => {
- this.newMessage(location + '/' + fileBrowser.files[0].name);
- this.loader.close();
- this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
- },
- (error) => {
- this.loader.close();
- this.dialog.errorReport(error.status, error.statusText, error._body);
- }
- );
- } else{
- this.loader.close();
- };
+ });
+
+ }
}
newMessage(message){
if(this.config.message){
(END)
我有一个现有的表单上传组件,可以将文件上传到后端,这工作正常,但问题是我没有任何视觉指示器来指示有多少文件,即已上传到服务器的文件的百分比。我相信这是我的这个组件中缺少的非常需要的功能。目前 Please wait
我可以修改 Please wait
并上传 % of file uploaded
吗?我在网上看到了很多使用进度条的答案,但我宁愿有一个对我有意义并适合我的具体情况的解决方案。
表格-upload.component.ts
import { Component, ViewChild, } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { Observer } from 'rxjs/Observer';
import { HttpClient} from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { FieldConfig } from '../../models/field-config.interface';
import { WebSocketService, DialogService } from '../../../../../../services/';
import { AppLoaderService } from '../../../../../../services/app-loader/app-loader.service';
import { Http } from '@angular/http';
import { MatSnackBar } from '@angular/material';
@Component({
selector: 'app-form-upload',
templateUrl: './form-upload.component.html',
styleUrls: ['../dynamic-field/dynamic-field.css', 'form-upload.component.css'],
})
export class FormUploadComponent {
@ViewChild('fileInput') fileInput;
config: FieldConfig;
group: FormGroup;
fieldShow: string;
public busy: Subscription[] = [];
public sub: Subscription;
public observer: Observer < any > ;
public jobId: Number;
public fileBrowser = true;
public apiEndPoint = '/_upload?auth_token=' + this.ws.token;
constructor(
protected ws: WebSocketService, protected http: Http, private loader: AppLoaderService,
private dialog:DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
upload(location = "/tmp/") {
if(this.config.updater && this.config.parent ){
this.config.updater(this, this.config.parent);
return;
}
this.loader.open();
const fileBrowser = this.fileInput.nativeElement;
if (fileBrowser.files && fileBrowser.files[0]) {
const formData: FormData = new FormData();
formData.append('data', JSON.stringify({
"method": "filesystem.put",
"params": [location + '/' + fileBrowser.files[0].name, { "mode": "493" }]
}));
formData.append('file', fileBrowser.files[0]);
this.http.post(this.apiEndPoint, formData).subscribe(
(data) => {
this.newMessage(location + '/' + fileBrowser.files[0].name);
this.loader.close();
this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
},
(error) => {
this.loader.close();
this.dialog.errorReport(error.status, error.statusText, error._body);
}
);
} else{
this.loader.close();
};
}
newMessage(message){
if(this.config.message){
this.config.message.newMessage(message);
}
}
}
表格-upload.component.html
<div id="{{config.name}}" class="dynamic-field form-input" [formGroup]="group" [ngClass]="fieldShow" [class.has-tooltip]="config.tooltip" *ngIf="!config.isHidden">
<div class="top">
<label>{{ config.placeholder | translate }}</label>
<tooltip *ngIf="config.tooltip" [message]="config.tooltip"></tooltip>
</div>
<div *ngIf="config.hideButton;else showButton">
<mat-card-content>
<input type="file" #fileInput accept="{{config.acceptedFiles}}" (change)="upload(config.fileLocation)" [formControlName]="config.name">
</mat-card-content>
</div>
<ng-template #showButton>
<mat-card-content>
<input type="file" #fileInput accept="{{config.acceptedFiles}}" [formControlName]="config.name">
</mat-card-content>
<mat-card-actions class="buttons">
<button mat-button type="button" (click)="upload(config.fileLocation)">Upload</button>
</mat-card-actions>
<mat-error *ngIf="config.hasErrors">{{config.errors}}</mat-error>
</ng-template>
</div>
app-loader.service.ts
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';
import { Observable } from 'rxjs/Rx';
import { AppLoaderComponent } from './app-loader.component';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../../translate-marker';
@Injectable()
export class AppLoaderService {
dialogRef: MatDialogRef<AppLoaderComponent>;
constructor(private dialog: MatDialog, private translate: TranslateService) { }
public open(title: string = T('Please wait')): Observable<boolean> {
this.translate.get(title).subscribe(t => {
this.dialogRef = this.dialog.open(AppLoaderComponent, {disableClose: true});
this.dialogRef.updateSize('200px', '200px');
this.dialogRef.componentInstance.title = t;
});
return this.dialogRef.afterClosed();
}
public close() {
this.dialogRef.close();
}
}
我在 https://malcoded.com/posts/angular-file-upload-component-with-express
查看src/app/upload/upload.service.ts
的源代码后想出的解决方案
如果有人感兴趣,这里是差异。
diff --git a/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts b/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
index 68be9cdf5..2aec0dd86 100644
--- a/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
+++ b/src/app/pages/common/entity/entity-form/components/form-upload/form-upload.component.ts
@@ -2,13 +2,13 @@ import { Component, ViewChild, } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import { Observer } from 'rxjs/Observer';
-import { HttpClient} from '@angular/common/http';
+import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { FieldConfig } from '../../models/field-config.interface';
-import { WebSocketService, DialogService } from '../../../../../../services/';
+import { WebSocketService } from '../../../../../../services/';
import { AppLoaderService } from '../../../../../../services/app-loader/app-loader.service';
-import { Http } from '@angular/http';
import { MatSnackBar } from '@angular/material';
+import { DialogService } from '../../../../../../services/';
@Component({
@@ -27,10 +27,10 @@ export class FormUploadComponent {
public jobId: Number;
public fileBrowser = true;
public apiEndPoint = '/_upload?auth_token=' + this.ws.token;
-
+
constructor(
- protected ws: WebSocketService, protected http: Http, private loader: AppLoaderService,
- private dialog:DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
+ protected ws: WebSocketService,protected http: HttpClient, private loader: AppLoaderService,
+ public dialog: DialogService, public snackBar: MatSnackBar, public translate: TranslateService) {}
upload(location = "/tmp/") {
if(this.config.updater && this.config.parent ){
@@ -47,21 +47,29 @@ export class FormUploadComponent {
"params": [location + '/' + fileBrowser.files[0].name, { "mode": "493" }]
}));
formData.append('file', fileBrowser.files[0]);
+ const req = new HttpRequest('POST', this.apiEndPoint, formData, {
+ reportProgress: true
+ });
+ this.http.request(req).subscribe(event => {
+ if (event.type === HttpEventType.UploadProgress) {
+ const percentDone = Math.round(100 * event.loaded / event.total);
+ const upload_msg = `${percentDone}% Uploaded`
+ this.loader.dialogRef.componentInstance.title = upload_msg;
+
+ } else if (event instanceof HttpResponse) {
+ if(event.statusText==="OK") {
+ this.newMessage(location + '/' + fileBrowser.files[0].name);
+ this.loader.close();
+ this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
+ }
+ };
+ },(error)=> {
+ this.loader.close();
+ this.dialog.errorReport("Error",error.statusText, error.message);
- this.http.post(this.apiEndPoint, formData).subscribe(
- (data) => {
- this.newMessage(location + '/' + fileBrowser.files[0].name);
- this.loader.close();
- this.snackBar.open("File upload complete.", 'close', { duration: 5000 });
- },
- (error) => {
- this.loader.close();
- this.dialog.errorReport(error.status, error.statusText, error._body);
- }
- );
- } else{
- this.loader.close();
- };
+ });
+
+ }
}
newMessage(message){
if(this.config.message){
(END)