【MinIO对象存储】- Angular + ng-zorro实现文件上传

后端API接口项目,请参考minio-server项目,使用springboot + minio-client框架实现存储服务。

前端使用Angular + ng-zorro实现,项目源码在这里Gitee

 模型(model)

模型在minio.model.ts文件中定义

export class Stat {
  etag!: string;
  size!: number;
  lastModified!: Date;
  retentionModal!: string;
  retentionRetainUtilDate!: Date;
  legalHold!: boolean;
  deleteMarker!: boolean;
  versionId!: string;
  contentTypeL!: string;
  userMetadata!: UserMetadata;

  constructor(data?: Partial<Stat>) {
    Object.assign(this, data);

    if (data?.lastModified != undefined) {
      this.lastModified = new Date(data.lastModified);
    }

    if (data?.retentionRetainUtilDate != undefined) {
      this.retentionRetainUtilDate = new Date(data.retentionRetainUtilDate);
    }

    if (data?.userMetadata != undefined) {
      this.userMetadata = new UserMetadata(data.userMetadata);
    }
  }
}

export class Item {
  etag!: string;
  objectName!: string;
  lastModified!: Date;
  owner!: string;
  size!: number;
  storageClass!: string;
  isLastest!: boolean;
  versionId!: string;
  isDir!: boolean;
  isDeleteMarker!: boolean;
  userMetadata!: UserMetadata;

  constructor(data?: Partial<Item>) {
    Object.assign(this, data);

    if (data?.lastModified != undefined) {
      this.lastModified = new Date(data.lastModified);
    }

    if (data?.userMetadata != undefined) {
      this.userMetadata = new UserMetadata(data.userMetadata);
    }
  }
}

export class UserMetadata {
  objectid!: string;
  bucket!: string;
  minioid!: string;
  filename!: string;
  filesize!: number;
  createdon!: Date;
  createdby!: string;
  contenttype!: string;

  constructor(data?: Partial<UserMetadata>) {
    Object.assign(this, data);

    if (data?.createdon != undefined) {
      this.createdon = new Date(data.createdon);
    }
  }
}

State, Item, UserMetadata是MinIO client 中的对象。

服务(sevice)

服务在minio.service.ts文件中定义

import { Injectable } from '@angular/core';
import { map, Observable, Subject, tap } from 'rxjs';

import { BaseHttpService } from '@core/base/base-http.service';

import { Item, Stat, UserMetadata } from './minio.model';

@Injectable({ providedIn: 'root' })
export class MinioService {
  private baseGetUrl = '/oss/v1/getservice';
  private baseListUrl = '/oss/v1/listservice';
  private basePutUrl = '/oss/v1/putservice';
  private baseRemoveUrl = '/oss/v1/removeservice';
  private baseStatUrl = '/oss/v1/statservice';
  private bucketName = 'megrez';

  private upload$ = new Subject<UserMetadata>();
  private remove$ = new Subject<UserMetadata>();

  constructor(private http: BaseHttpService) {}

  public get onUpload(): Observable<UserMetadata> {
    return this.upload$.asObservable();
  }

  public get onRemove(): Observable<UserMetadata> {
    return this.remove$.asObservable();
  }

  upload(formData: FormData): Observable<UserMetadata> {
    return this.http.post<UserMetadata>(`${this.basePutUrl}/object/${this.bucketName}`, formData).pipe(
      map(e => new UserMetadata(e)),
      tap(e => {
        this.upload$.next(e);
      })
    );
  }

  download(objectId: string): Observable<Blob> {
    return this.http.get(`${this.baseGetUrl}/object/${this.bucketName}/${objectId}`, null, {
      responseType: 'blob'
    });
  }

  downloads(objectIds: string[]): Observable<Blob> {
    const queryParams = { objectIds: objectIds };
    return this.http.get(`${this.baseGetUrl}/objects/${this.bucketName}`, queryParams, { responseType: 'blob' });
  }

  getPresignedObjectUrl(objectId: string, method: string): Observable<string> {
    return this.http.get(`${this.baseGetUrl}/presigned-url/${this.bucketName}/${objectId}/${method}`, null, {
      responseType: 'text'
    });
  }

  delete(objectId: string): Observable<UserMetadata> {
    const url = `${this.baseRemoveUrl}/object/${this.bucketName}/${objectId}`;
    return this.http.delete<UserMetadata>(url).pipe(
      map(e => new UserMetadata(e)),
      tap(e => {
        this.remove$.next(new UserMetadata(e));
      })
    );
  }

  list(prefix: string, includeUserMetadata?: boolean): Observable<Item[]> {
    const queryParams = { prefix: prefix, includeUserMetadata: includeUserMetadata };
    return this.http
      .get<Item[]>(`${this.baseListUrl}/objects/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new Item(e))));
  }

  getStat(objectId: string): Observable<Stat> {
    return this.http.get<Stat>(`${this.baseStatUrl}/state/${this.bucketName}/${objectId}`).pipe(map(e => new Stat(e)));
  }

  getStats(objectIds: string[]): Observable<Stat[]> {
    const queryParams = { objectIds: objectIds };
    return this.http
      .get<Stat[]>(`${this.baseStatUrl}/state/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new Stat(e))));
  }

  getUserMetadata(objectId: string): Observable<UserMetadata> {
    return this.http
      .get<UserMetadata>(`${this.baseStatUrl}/usermetadata/${this.bucketName}/${objectId}`)
      .pipe(map(e => new UserMetadata(e)));
  }

  getUserMetadatas(objectIds: string[]): Observable<UserMetadata[]> {
    const queryParams = { objectIds: objectIds };
    return this.http
      .get<UserMetadata[]>(`${this.baseStatUrl}/usermetadata/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new UserMetadata(e))));
  }
}

功能包括,文件上传,下载,获取用户元数据等。

功能演示

演示功能在minio-action.component.html及minio-action.component.ts中

import { Component } from '@angular/core';
import { forkJoin, Observable } from 'rxjs';

import { FileService } from '@core/base';
import { MinioService, UserMetadata } from '@core/oss/minio';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzUploadFile } from 'ng-zorro-antd/upload';

@Component({
  selector: 'app-minio-action',
  templateUrl: './minio-action.component.html'
})
export class MinioActionComponent {
  fileList: NzUploadFile[] = [];
  userMatadatas: UserMetadata[] = [];

  downloadIds: string = '';
  presignedUrlId?: string;
  deleteId?: string;
  isLoading = false;
  presignedUrl?: string;
  method: string = 'GET';

  constructor(private filesSrc: FileService, private minioSrc: MinioService, private messageSrc: NzMessageService) {}

  beforeUpload = (file: NzUploadFile): boolean => {
    this.fileList = this.fileList.concat(file);
    return false;
  };

  handleUpload(): void {
    const obserList: Array<Observable<any>> = [];
    this.fileList.forEach((file: any) => {
      const formData = new FormData();
      formData.append('file', file);
      obserList.push(this.minioSrc.upload(formData));
    });
    this.isLoading = true;
    forkJoin(obserList).subscribe({
      next: (data: any) => {
        this.isLoading = false;
        this.fileList = [];
        this.userMatadatas = this.userMatadatas.concat(data);
        this.messageSrc.success('upload successfully.');
      },
      error: () => {
        this.isLoading = false;
        this.messageSrc.error('upload failed.');
      }
    });
  }

  download(): void {
    const idList = this.downloadIds.split(',').filter(e => e.trim().length > 0);
    if (idList.length === 0) {
      return;
    }

    this.isLoading = true;
    if (idList.length === 1) {
      this.minioSrc.download(idList[0]).subscribe(data => {
        this.filesSrc.saveAsFile(data, '');
        this.isLoading = false;
      });
    } else {
      this.minioSrc.downloads(idList).subscribe(data => {
        this.filesSrc.saveAsFile(data, '');
        this.isLoading = false;
      });
    }
  }

  delete(): void {
    if (!this.deleteId) {
      return;
    }

    this.isLoading = true;
    this.minioSrc.delete(this.deleteId).subscribe(() => {
      this.messageSrc.success('删除成功');
      this.isLoading = false;
    });
  }

  getPresignedUrl(): void {
    if (!this.presignedUrlId) {
      return;
    }

    this.isLoading = true;
    this.minioSrc.getPresignedObjectUrl(this.presignedUrlId, this.method).subscribe(data => {
      this.presignedUrl = data;
      this.isLoading = false;
    });
  }
}
<nz-spin [nzSpinning]="isLoading">
  <nz-upload [(nzFileList)]="fileList" [nzBeforeUpload]="beforeUpload">
    <button nz-button>
      <span nz-icon nzType="upload"></span>
      Select File
    </button>
  </nz-upload>
  <button
    nz-button
    [nzType]="'primary'"
    (click)="handleUpload()"
    [disabled]="fileList.length === 0"
    style="margin-top: 16px"
  >
    {{ isLoading ? 'Uploading' : 'Start Upload' }}
  </button>
  <div style="margin-top: 8px">
    <textarea rows="10" nz-input [ngModel]="userMatadatas | json" *ngIf="userMatadatas.length > 0"></textarea>
    <nz-empty *ngIf="userMatadatas.length === 0"></nz-empty>
  </div>

  <div style="margin-top: 25px">
    <nz-space>
      <input
        *nzSpaceItem
        nz-input
        placeholder="输入objectId, 多个使用','相隔"
        [(ngModel)]="downloadIds"
        style="width: 300px"
      />
      <button *nzSpaceItem nz-button nzType="primary" (click)="download()">下载文件</button>
    </nz-space>
  </div>

  <div style="margin-top: 25px">
    <nz-space>
      <input *nzSpaceItem nz-input placeholder="输入一个objectId" [(ngModel)]="deleteId" style="width: 300px" />
      <button *nzSpaceItem nz-button nzType="primary" (click)="delete()"> 删除 </button>
    </nz-space>
  </div>

  <div style="margin-top: 25px">
    <nz-space>
      <input *nzSpaceItem nz-input placeholder="输入一个objectId" [(ngModel)]="presignedUrlId" style="width: 300px" />
      <nz-radio-group *nzSpaceItem [(ngModel)]="method">
        <label nz-radio nzValue="GET">GET</label>
        <label nz-radio nzValue="POST">POST</label>
        <label nz-radio nzValue="PUT">PUT</label>
        <label nz-radio nzValue="DELETE">DELETE</label>
      </nz-radio-group>
      <button *nzSpaceItem nz-button nzType="primary" (click)="getPresignedUrl()"> 获取PresignedUrl </button>
    </nz-space>

    <textarea style="margin-top: 8px" rows="4" nz-input [ngModel]="presignedUrl" readonly></textarea>
  </div>
</nz-spin>

功能截图

文件上传:

其他功能:

 获取元数据:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值