需要帮忙! Angular 2 File/Image 通过 API 上传

Need Help! Angular 2 File/Image upload via API

我正在使用 Angular2 上传 image/file。我不喜欢添加任何插件来上传 angular2 中的文件。有没有任何简单的方法可以使用 Angular2 通过 API 上传图像而无需任何第三方扩展?

NB: i am using laravel as server

这是从 Angular 2 上传图片(或文件)到服务器时需要 done/considered 的内容。

  1. 使用以下元素创建图片上传组件。 模板:

<div class="image-upload"
     fileDrop
     [accept]="['image/*']"
     (isFileOver)="fileOver($event)"
     (fileDrop)="fileChange($event)"
     [ngClass]="{'file-is-over': isFileOver}"
>
  <div class="file-upload hr-inline-group">
    <label class="upload-button">
      <span>{{buttonCaption}}</span>
      <input
        type="file"
        accept="image/*"
        (change)="fileChange(input.files)"
        #input>
    </label>   

    <div class="drag-box-message">{{dropBoxMessage}}</div>
  </div>
    <div *ngIf="fileSizeExceeded" class="drag-box-message">
        <p>Please upload a file less than 2 MB.</p>
    </div>

  <div *ngIf="preview" class="image-container hr-inline-group">
    <div
      class="image"
      *ngFor="let file of files"
      [ngStyle]="{'background-image': 'url('+ file.src +')'}"
    >
      <div *ngIf="file.pending" class="loading-overlay">
        <div class="spinningCircle"></div>
      </div>
      <div *ngIf="!file.pending" class="x-mark" (click)="deleteFile(file)">
        <span class="close"></span>
      </div>
    </div>
  </div>
</div>

  1. 您的组件class,需要导出。

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { ImageService, Header } from "../../../services/content/image.service";

export class FileHolder {
    public serverResponse: any;
    public pending: boolean = false;
    constructor(public src: string, public file: File) { }
}

@Component({
    selector: 'image-upload',
    templateUrl: "app/components/controls/image-upload/image-upload.component.html",
    styles: [
        `
.image-upload {
  --common-radius: 3px;
  --active-color: #33CC99;
  position: relative;
  border-radius: var(--common-radius);
  border: #d0d0d0 dashed 1px;
  font-family: sans-serif;
}

.file-is-over {
  border-color: var(--active-color);
  border-style: solid;
}

.hr-inline-group:after {
  display: table;
  clear: both;
  content: "";
}

.file-upload {
  padding: 16px;
  background-color: #f8f8f8;
}

.drag-box-message {
  float: left;
  display: inline-block;
  margin-left: 12px;
  padding-top: 14px;
  color: #9b9b9b;
  font-weight: 600;
}

label.upload-button input[type=file] {
  display: none;
  position: fixed;
  top: -99999px;
}

.upload-button {
  cursor: pointer;
  background-color: var(--active-color);
  padding: 10px;
  color: white;
  font-size: 1.25em;
  font-weight: 500;
  text-transform: uppercase;
  display: inline-block;
  float: left;
  
  -webkit-box-shadow: 2px 2px 4px 0px rgba(148,148,148,0.6);
  -moz-box-shadow: 2px 2px 4px 0px rgba(148,148,148,0.6);
  box-shadow: 2px 2px 4px 0px rgba(148,148,148,0.6);
}

.upload-button:active span{
  position: relative;
  display: block;
  top: 1px;
}

.image-container {
  background-color: #fdfdfd;
  padding: 0 10px 0 10px;
}

.image {
  float: left;
  display: inline-block;
  margin: 6px;
  width: 86px;
  height: 86px;
  background: center center no-repeat;
  background-size: contain;
  position: relative;
}

.x-mark {
  width: 20px;
  height: 20px;
  text-align: center;
  cursor: pointer;
  border-radius: 2px;
  float: right;
  background-color: black;
  opacity: .7;
  color: white;
  margin: 2px;
}

.close {
  width: 20px;
  height: 20px;
  opacity: .7;
  position: relative;
  padding-right: 3px;
}
.x-mark:hover .close {
  opacity: 1;
}
.close:before, .close:after {
  border-radius: 2px;
  position: absolute;
  content: '';
  height: 16px;
  width: 2px;
  top: 2px;
  background-color: #FFFFFF;
}

.close:before {
  transform: rotate(45deg);
}

.close:after {
  transform: rotate(-45deg);
}

.loading-overlay {
  position: absolute;
  top: 0; left: 0; bottom: 0; right: 0;
  background-color: black;
  opacity: .7;
}

.spinningCircle {
  height: 30px;
  width: 30px;
  margin: auto;
  position: absolute;
  top: 0; left: 0; bottom: 0; right: 0;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0);
  border-top: 3px solid white;
  border-right: 3px solid white;
  -webkit-animation: spinner 2s infinite cubic-bezier(0.085, 0.625, 0.855, 0.360);
  animation: spinner 2s infinite cubic-bezier(0.085, 0.625, 0.855, 0.360);
}

@-webkit-keyframes spinner {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@keyframes spinner {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);

  }
}
`
    ]
})
export class ImageUploadComponent {
    @Input() max: number = 100;
    @Input() url: string;
    @Input() headers: Header[];
    @Input() preview: boolean = true;

    @Output()
    isPending: EventEmitter<boolean> = new EventEmitter<boolean>();
    @Output()
    onFileUploadFinish: EventEmitter<FileHolder> = new EventEmitter<FileHolder>();
    @Output()
    onRemove: EventEmitter<FileHolder> = new EventEmitter<FileHolder>();

    private files: FileHolder[] = [];

    private fileCounter: number = 0;
    private pendingFilesCounter: number = 0;
    private fileSizeExceeded: boolean = false;

    private isFileOver: boolean = false;

    @Input()
    buttonCaption: string = "Select Images";
    @Input()
    dropBoxMessage: string = "Drop your images here!";

    constructor(private imageService: ImageService) { }

    ngOnInit() {
        this.imageService.setUrl(this.url);
    }

    fileChange(files) {
        let remainingSlots = this.countRemainingSlots();
        let filesToUploadNum = files.length > remainingSlots ? remainingSlots : files.length;

        if (this.url && filesToUploadNum != 0) {
            this.isPending.emit(true);
        }

        this.fileCounter += filesToUploadNum;

        this.uploadFiles(files, filesToUploadNum);
    }

    private uploadFiles(files, filesToUploadNum) {
        for (let i = 0; i < filesToUploadNum; i++) {
            let file = files[i];

            console.log('FILE SIZE: ', file.size);

            if (file.size > 3000000) {
                this.fileSizeExceeded = true;
                return;
            }

            let img = document.createElement('img');
            img.src = window.URL.createObjectURL(file);

            let reader = new FileReader();
            reader.addEventListener('load', (event: any) => {
                let fileHolder: FileHolder = new FileHolder(event.target.result, file);

                fileHolder.serverResponse = `good boy: ${i}`;

                this.uploadSingleFile(fileHolder);

                this.files.push(fileHolder);

            }, false);


            reader.readAsDataURL(file);
        }
    }

    private uploadSingleFile(fileHolder: FileHolder) {
        if (this.url) {
            this.pendingFilesCounter++;
            fileHolder.pending = true;

            this.imageService.postImage(fileHolder.file, this.headers).subscribe(response => {
                fileHolder.serverResponse = response;
                this.onFileUploadFinish.emit(fileHolder);
                fileHolder.pending = false;
                if (--this.pendingFilesCounter == 0) {
                    this.isPending.emit(false);
                }
            });

        } else {
            this.onFileUploadFinish.emit(fileHolder);
        }
    }

    private deleteFile(file: FileHolder): void {
        let index = this.files.indexOf(file);
        this.files.splice(index, 1);
        this.fileCounter--;

        this.onRemove.emit(file);
    }

    fileOver(isOver) {
        this.isFileOver = isOver;
    }

    private countRemainingSlots() {
        return this.max - this.fileCounter;
    }
}

  1. 将其导入您的 App 模块,并将其作为声明的一部分包含在内。

  2. 现在这个组件可以在任何地方使用来上传图片。

  3. 此组件负责添加请求 headers,其中可以包含服务器将使用的身份验证令牌。

希望对您有所帮助。

  1. 模板

<input type="file" (change)="fileChangeEvent($event)" placeholder="Upload file..." />
<button type="button" (click)="upload()">Upload</button>

  1. 服务

export class FileUpload{
/* This method will worked for single file upload, if you need multipule file upload then replace formData.append("uploads", files[i], files[i].name); to formData.append("uploads[]", files[i], files[i].name);
*/
makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
        return new Promise((resolve, reject) => {
            var formData: any = new FormData();
            var xhr = new XMLHttpRequest();
            for(var i = 0; i < files.length; i++) {
                formData.append("uploads", files[i], files[i].name);
            }
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) {
                        resolve(JSON.parse(xhr.response));
                    } else {
                        reject(xhr.response);
                    }
                }
            }
            xhr.open("POST", url, true);
            xhr.send(formData);
        });
    }
}

  1. 组件

export class AppComponent {
  filesToUpload: Array<File>;

  constructor(private _fileUpload:FileUpload) {
        this.filesToUpload = [];
  }

  upload() {
        this._fileUpload.makeFileRequest("/api/admin/upload-slider-image", [], this.filesToUpload).then((result) => {
            console.log(result);
        }, (error) => {
            console.error(error);
        });
    }

    fileChangeEvent(fileInput: any){
        this.filesToUpload = <Array<File>> fileInput.target.files;
    }
 }