angular文件上传,支持多文件、拖拽、重试以及上传进度

Demo效果

1. Dependencies

  • @angular/cdk
  • @angular/material

2. file-upload组件

  • file-upload.component.html

cloud_upload

Click or drag file to this area to upload

Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files

  • file-upload.component.scss
@import '~@angular/material/theming';

.upload-container {
  display: flex;
  flex-direction: column;
  align-content: center;
  justify-content: center;
  text-align: center;
  background: #fafafa;
  border: 1px dashed #d9d9d9;
  padding: 16px 0;
  margin: 20px;
  cursor: pointer;
  p {
    margin: 0;
    padding: 0;
  }
  .icon {
    margin-bottom: 20px;
  }
  .title {
    margin: 0 0 4px;
    color: rgba(0,0,0,.85);
    font-size: 16px;
  }
  .desc {
    color: rgba(0,0,0,.45);
    font-size: 14px;
  }
}

.hovering {
  border: 1px dashed tomato;
}

ul,
li {
    margin: 0 20px;
    padding: 0;
    list-style: none;
    mat-progress-bar {
        border-radius: 10px;
    }
    .file-label {
        display: inline-flex;
        vertical-align: middle;
        font-size: 12px;
        line-height: 18px;
        justify-content: flex-end;
        width: 100%;
        padding-top: 5px;
        mat-icon {
            font-size: 18px;
            text-align: center;
        }
        mat-icon:hover {
            color: map-get($mat-red, 500);
        }
        a {
            margin-left: 4px;
            cursor: pointer;
        }
    }
}
  • file-upload.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
import {
  trigger,
  state,
  style,
  animate,
  transition
} from "@angular/animations";
import {
  HttpClient,
  HttpResponse,
  HttpRequest,
  HttpEventType,
  HttpErrorResponse
} from "@angular/common/http";
import { of } from "rxjs";
import { catchError, last, map, tap } from "rxjs/operators";
import { FileUploadModel } from './file-upload.model';

@Component({
  selector: "app-file-upload",
  templateUrl: "./file-upload.component.html",
  styleUrls: ["./file-upload.component.scss"],
  animations: [
    trigger("fadeInOut", [
      state("in", style({ opacity: 100 })),
      transition("* => void", [animate(300, style({ opacity: 0 }))])
    ])
  ]
})
export class FileUploadComponent implements OnInit {
  /** Link text */
  @Input() text = "Upload";
  /** Name used in form which will be sent in HTTP request. */
  @Input() param = "file";
  /** Target URL for file uploading. */
  @Input() target = "https://file.io";
  /** File extension that accepted, same as 'accept' of . By the default, it's set to 'image/*'. */
  @Input() accept = "image/*";
  /** Allow you to add handler after its completion. Bubble up response text from remote. */
  @Output() complete = new EventEmitter();

  private files: Array = [];

  constructor(private _http: HttpClient) {}

  ngOnInit() {}

  onDrop(files: FileList) {
    for (let index = 0; index < files.length; index++) {
      const file = files[index];
      this.files.push({
        data: file,
        state: "in",
        inProgress: false,
        progress: 0,
        canRetry: false,
        canCancel: true
      });
      this.uploadFiles();
    }
  }

  onClick() {
    const fileUpload = document.getElementById(
      "fileUpload"
    ) as HTMLInputElement;
    fileUpload.onchange = () => {
      for (let index = 0; index < fileUpload.files.length; index++) {
        const file = fileUpload.files[index];
        this.files.push({
          data: file,
          state: "in",
          inProgress: false,
          progress: 0,
          canRetry: false,
          canCancel: true
        });
      }
      this.uploadFiles();
    };
    fileUpload.click();
  }

  cancelFile(file: FileUploadModel) {
    file.sub.unsubscribe();
    this.removeFileFromArray(file);
  }

  retryFile(file: FileUploadModel) {
    this.uploadFile(file);
    file.canRetry = false;
  }

  private uploadFile(file: FileUploadModel) {
    const fd = new FormData();
    fd.append(this.param, file.data);

    const req = new HttpRequest("POST", this.target, fd, {
      reportProgress: true
    });

    file.inProgress = true;
    file.sub = this._http
      .request(req)
      .pipe(
        map(event => {
          switch (event.type) {
            case HttpEventType.UploadProgress:
              file.progress = Math.round((event.loaded * 100) / event.total);
              break;
            case HttpEventType.Response:
              return event;
          }
        }),
        tap(message => {}),
        last(),
        catchError((error: HttpErrorResponse) => {
          file.inProgress = false;
          file.canRetry = true;
          return of(`${file.data.name} upload failed.`);
        })
      )
      .subscribe((event: any) => {
        if (typeof event === "object") {
          this.removeFileFromArray(file);
          this.complete.emit(event.body);
        }
      });
  }

  private uploadFiles() {
    const fileUpload = document.getElementById(
      "fileUpload"
    ) as HTMLInputElement;
    fileUpload.value = "";

    this.files.forEach(file => {
      this.uploadFile(file);
    });
  }

  private removeFileFromArray(file: FileUploadModel) {
    const index = this.files.indexOf(file);
    if (index > -1) {
      this.files.splice(index, 1);
    }
  }

}
  • file-upload.model.ts
import { Subscription } from "rxjs";

export class FileUploadModel {
  data: File;
  state: string;
  inProgress: boolean;
  progress: number;
  canRetry: boolean;
  canCancel: boolean;
  sub?: Subscription;
}

3. drop指令

  • dropzone.directive.ts
import { Directive, HostListener, Output, EventEmitter } from "@angular/core";

@Directive({
  selector: "[dropzone]"
})
export class DropzoneDirective {
  @Output() dropped = new EventEmitter();
  @Output() hovered = new EventEmitter();

  @HostListener("drop", ["$event"])
  onDrop($event) {
    $event.preventDefault();
    this.dropped.emit($event.dataTransfer.files);
    this.hovered.emit(false);
  }

  @HostListener("dragover", ["$event"])
  onDragOver($event) {
    $event.preventDefault();
    this.hovered.emit(true);
  }

  @HostListener("dragleave", ["$event"])
  onDragLeave($event) {
    $event.preventDefault();
    this.hovered.emit(false);
  }
}

4. Demo Stackblitz 地址

angular-drag-file-upload


声明:文章参考进行改进:https://stackblitz.com/edit/angular-material-file-upload-jtcn1z

你可能感兴趣的:(angular文件上传,支持多文件、拖拽、重试以及上传进度)