如何使用 AngularFire2 和 Firebase 存储上传文件数组?
Posted
技术标签:
【中文标题】如何使用 AngularFire2 和 Firebase 存储上传文件数组?【英文标题】:How to upload array of files using AngularFire2 and Firebase Storage? 【发布时间】:2019-02-09 11:56:24 【问题描述】:我一直在努力寻找这个问题的答案。大多数示例,包括 AngularFire2 文档和来自 angularfirebase.com 的精彩教程,仅侧重于当时上传一个文件。我想建立一个照片库 cms,所以这个选项效率低下,至少可以说......
对于我最初的问题,我从 Leon Radley 那里得到了一个答案,感谢他的回答,它帮助我重新思考了这种方法。
尽管如此,该解决方案对我来说并不是最优的。我决定回答我自己的问题,这样它可能会对某人有所帮助,甚至更好,有人会提出更好的解决方案。
随着我提供更多功能,我将更新我的答案。我只是重写angularfirebase.com 提供的解决方案以利用文件数组。
注意这里用到了Custom Drop Event和FileList Directive,更多细节请参考源码
原解决方案:
文件上传.component.ts
import Component, OnInit from '@angular/core';
import AngularFireStorage, AngularFireUploadTask from 'angularfire2/storage';
import Observable from 'rxjs/Observable';
@Component(
selector: 'file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.scss']
)
export class FileUploadComponent
// Main task
task: AngularFireUploadTask;
// Progress monitoring
percentage: Observable<number>;
snapshot: Observable<any>;
// Download URL
downloadURL: Observable<string>;
// State for dropzone CSS toggling
isHovering: boolean;
constructor(private storage: AngularFireStorage, private db: AngularFirestore)
toggleHover(event: boolean)
this.isHovering = event;
startUpload(event: FileList)
// The File object
const file = event.item(0)
// Client-side validation example
if (file.type.split('/')[0] !== 'image')
console.error('unsupported file type :( ')
return;
// The storage path
const path = `test/$new Date().getTime()_$file.name`;
// Totally optional metadata
const customMetadata = app: 'My AngularFire-powered PWA!' ;
// The main task
this.task = this.storage.upload(path, file, customMetadata )
// Progress monitoring
this.percentage = this.task.percentageChanges();
this.snapshot = this.task.snapshotChanges()
// The file's download URL
this.downloadURL = this.task.downloadURL();
// Determines if the upload task is active
isActive(snapshot)
return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes
文件上传.component.html
<div class="dropzone"
dropZone
(hovered)="toggleHover($event)"
(dropped)="startUpload($event)"
[class.hovering]="isHovering">
<h3>AngularFire Drop Zone</h3>
<div class="file">
<label class="file-label">
<input class="file-input" type="file" (change)="startUpload($event.target.files)">
<span class="file-cta">
<span class="file-icon">
<i class="fa fa-upload"></i>
</span>
<span class="file-label">
or choose a file…
</span>
</span>
</label>
</div>
</div>
<div *ngIf="percentage | async as pct">
<progress class="progress is-info"
[value]="pct"
max="100">
</progress>
pct | number %
</div>
<div *ngIf="snapshot | async as snap">
snap.bytesTransferred | fileSize of snap.totalBytes | fileSize
<div *ngIf="downloadURL | async as url">
<h3>Results!</h3>
<img [src]="url"><br>
<a [href]="url" target="_blank" rel="noopener">Download Me!</a>
</div>
<button (click)="task.pause()" class="button is-warning" [disabled]="!isActive(snap)">Pause</button>
<button (click)="task.cancel()" class="button is-danger" [disabled]="!isActive(snap)">Cancel</button>
<button (click)="task.resume()" class="button is-info" [disabled]="!(snap?.state === 'paused')">Resume</button>
</div>
【问题讨论】:
【参考方案1】:我刚刚构建了这样的东西:)
我将 percentChanges() observables 存储在一个数组中,并在模板中循环遍历它们
<div class="tasks">
<mat-progress-bar
class="mt-2"
*ngFor="let progress$ of progressObservables"
mode="determinate"
[value]="progress$ | async"
></mat-progress-bar>
</div>
然后我使用 Promise.all 函数等待所有这些并关闭对话框。 因为所有的 Observables 也是 Promise,所以效果很好。
this.tasks: AngularFireUploadTask[] = ...;
this.progressObservables = this.tasks.map(t => t.percentageChanges());
Promise.all(this.tasks).then(() => this.dialogRef.close());
【讨论】:
是的,很好用!遗憾的是没有示例如何以反应方式进行操作。我喜欢暂停和恢复上传的可能性......就像这里显示的那样:angularfirebase.com/lessons/… 如果您循环遍历任务而不是 percentChanges(),您将可以访问 pause() 函数,并且应该能够设计一个可以完成此任务的 UI。【参考方案2】:到目前为止,我设法复制了以下功能:
上传 快照(bytesTransferred / totalBytes) 进展文件上传.component.ts
@Component(
selector: 'app-file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.scss'],
)
export class FileUploadComponent
tasks$: Observable<any>
progresses$: any
snapshots$: any
isHovering: boolean
constructor(
private storage: AngularFireStorage,
private db: AngularFirestore,
)
toggleHover(event: boolean)
this.isHovering = event
startUpload(event: HTMLInputEvent)
this.tasks$ = from([Array.from(event.target.files)]).pipe(
map(files =>
files.map(file =>
const path = `test/$new Date().getTime()_$file.name`
const customMetadata = app: 'My AngularFire-powered PWA!'
return this.storage.upload(path, file, customMetadata )
),
),
)
this.snapshots$ = this.tasks$.pipe(
map(files =>
files.map(file =>
file.snapshotChanges(),
),
)
)
this.progresses$ = this.tasks$.pipe(
map(files =>
files.map(file =>
file.percentageChanges()
),
)
)
文件上传.component.html
<div class="dropzone"
appDropZone
(hovered)="toggleHover($event)"
(dropped)="startUpload($event)"
[class.hovering]="isHovering">
<h3>AngularFire Drop Zone</h3>
<div class="file">
<label class="file-label">
<input class="file-input" type="file" (change)="startUpload($event)"
multiple>
<span class="file-cta">
<span class="file-icon">
<i class="fa fa-upload"></i>
</span>
<span class="file-label">
or choose a file…
</span>
</span>
</label>
</div>
</div>
<div>
<ul>
<li *ngFor="let progress of progresses$ | async">
<progress
class="progress is-info"
[value]="progress | async"
max="100">
</progress>
progress | async | number %
</li>
</ul>
</div>
<div>
<ul>
<li *ngFor="let snap of snapshots$ | async">
<p *ngIf="snap | async">
(snap | async)?.bytesTransferred | fileSize of
(snap | async)?.totalBytes | fileSize
</p>
</li>
</ul>
</div>
【讨论】:
以上是关于如何使用 AngularFire2 和 Firebase 存储上传文件数组?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用angularfire2检查孩子是不是存在于firebase列表中
如何使用 angularfire2 valuechanges 方法获取随机数据
如何解决 rxjs Typescript 错误(Ionic 3,angularfire2)