Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 696e36e

Browse files
committedMar 6, 2025·
feat(module:upload): send event when file is not authorize
1 parent 58e87a3 commit 696e36e

7 files changed

+148
-49
lines changed
 

‎components/upload/doc/index.en-US.md

+31-30
Large diffs are not rendered by default.

‎components/upload/doc/index.zh-CN.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { NzUploadModule } from 'ng-zorro-antd/upload';
5353
| `[nzPreviewIsImage]` | 自定义预览文件是否有效图像,一般用于图像 URL 为非标准格式;注意:务必使用 `=>` 定义处理方法。 | `(file: NzUploadFile) => boolean` | - |
5454
| `[nzRemove]` | 点击移除文件时的回调,返回值为 false 时不移除。支持返回 `Observable` 对象;注意:务必使用 `=>` 定义处理方法。 | `(file: NzUploadFile) => boolean \| Observable<boolean>` | - |
5555
| `(nzChange)` | 上传文件改变时的状态 | `EventEmitter<NzUploadChangeParam>` | - |
56+
| `(nzFilesNotAuthorize)` | 当文件不符合标准过滤器时,将执行回调函数 | `EventEmitter<NzFileNotAuthorize[]>` | - |
5657
| `[nzDownload]` | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页 | `(file: NzUploadFile) => void` | 跳转新标签页 |
5758
| `[nzTransformFile]` | 在上传之前转换文件。支持返回一个 Observable 对象 | `(file: NzUploadFile) => NzUploadTransformFileType` | - |
5859
| `[nzIconRender]` | 自定义显示 icon | `TemplateRef<{ $implicit: NzUploadFile }>` | - |

‎components/upload/interface.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,33 @@ export interface ZipButtonOptions {
5858
action?: string | ((file: NzUploadFile) => string | Observable<string>);
5959
directory?: boolean;
6060
openFileDialogOnClick?: boolean;
61+
6162
beforeUpload?(file: NzUploadFile, fileList: NzUploadFile[]): boolean | Observable<NzSafeAny>;
63+
6264
customRequest?(item: NzSafeAny): Subscription;
65+
6366
data?: {} | ((file: NzUploadFile) => {} | Observable<{}>);
6467
headers?: {} | ((file: NzUploadFile) => {} | Observable<{}>);
6568
name?: string;
6669
multiple?: boolean;
6770
withCredentials?: boolean;
6871
filters?: UploadFilter[];
72+
6973
transformFile?(file: NzUploadFile): NzUploadTransformFileType;
74+
7075
onStart?(file: NzUploadFile): void;
76+
7177
onProgress?(e: NzSafeAny, file: NzUploadFile): void;
78+
7279
onSuccess?(ret: NzSafeAny, file: NzUploadFile, xhr: NzSafeAny): void;
80+
7381
onError?(err: NzSafeAny, file: NzUploadFile): void;
7482
}
7583

7684
export interface UploadFilter {
7785
name: string;
78-
fn(fileList: NzUploadFile[]): NzUploadFile[] | Observable<NzUploadFile[]>;
86+
87+
fn(fileList: NzUploadFile[], filesNotAuthorize?: NzFileNotAuthorize[]): NzUploadFile[] | Observable<NzUploadFile[]>;
7988
}
8089

8190
export interface NzUploadXHRArgs {
@@ -86,9 +95,17 @@ export interface NzUploadXHRArgs {
8695
postFile: string | Blob | File | NzUploadFile;
8796
data?: IndexableObject;
8897
withCredentials?: boolean;
98+
8999
onProgress?(e: NzSafeAny, file: NzUploadFile): void;
100+
90101
onSuccess?(ret: NzSafeAny, file: NzUploadFile, xhr: NzSafeAny): void;
102+
91103
onError?(err: NzSafeAny, file: NzUploadFile): void;
92104
}
93105

106+
export interface NzFileNotAuthorize {
107+
reason: 'size' | 'extension';
108+
file: NzUploadFile | File;
109+
}
110+
94111
export type NzIconRenderTemplate = TemplateRef<{ $implicit: NzUploadFile }>;

‎components/upload/upload-btn.component.ts

+40-8
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,26 @@
55

66
import { ENTER } from '@angular/cdk/keycodes';
77
import { HttpClient, HttpEvent, HttpEventType, HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http';
8-
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core';
9-
import { Observable, Subject, Subscription, of } from 'rxjs';
8+
import {
9+
Component,
10+
ElementRef,
11+
EventEmitter,
12+
inject,
13+
Input,
14+
OnDestroy,
15+
OnInit,
16+
Output,
17+
ViewChild,
18+
ViewEncapsulation
19+
} from '@angular/core';
20+
import { Observable, of, Subject, Subscription } from 'rxjs';
1021
import { map, switchMap, takeUntil, tap } from 'rxjs/operators';
1122

1223
import { warn } from 'ng-zorro-antd/core/logger';
1324
import { NzSafeAny } from 'ng-zorro-antd/core/types';
1425
import { fromEventOutsideAngular } from 'ng-zorro-antd/core/util';
1526

16-
import { NzUploadFile, NzUploadXHRArgs, ZipButtonOptions } from './interface';
27+
import { NzFileNotAuthorize, NzUploadFile, NzUploadXHRArgs, ZipButtonOptions } from './interface';
1728

1829
@Component({
1930
selector: '[nz-upload-btn]',
@@ -34,8 +45,10 @@ export class NzUploadBtnComponent implements OnInit, OnDestroy {
3445
reqs: Record<string, Subscription> = {};
3546
private destroy = false;
3647
private destroy$ = new Subject<void>();
48+
private _nzFileNotAuthorize: NzFileNotAuthorize[] = [];
3749
@ViewChild('file', { static: true }) file!: ElementRef<HTMLInputElement>;
3850
@Input() options!: ZipButtonOptions;
51+
@Output() readonly nzFilesNotAuthorize = new EventEmitter<NzFileNotAuthorize[]>();
3952

4053
onClick(): void {
4154
if (this.options.disabled || !this.options.openFileDialogOnClick) {
@@ -46,39 +59,55 @@ export class NzUploadBtnComponent implements OnInit, OnDestroy {
4659

4760
// skip safari bug
4861
onFileDrop(e: DragEvent): void {
62+
this._nzFileNotAuthorize = [];
4963
if (this.options.disabled || e.type === 'dragover') {
5064
e.preventDefault();
5165
return;
5266
}
5367
if (this.options.directory) {
5468
this.traverseFileTree(e.dataTransfer!.items);
5569
} else {
56-
const files: File[] = Array.prototype.slice
57-
.call(e.dataTransfer!.files)
58-
.filter((file: File) => this.attrAccept(file, this.options.accept));
70+
const files: File[] = Array.prototype.slice.call(e.dataTransfer!.files).filter((file: File) => {
71+
if (this.attrAccept(file, this.options.accept)) {
72+
return true;
73+
} else {
74+
this._nzFileNotAuthorize.push({ reason: 'extension', file });
75+
return false;
76+
}
77+
});
5978
if (files.length) {
6079
this.uploadFiles(files);
6180
}
81+
if (this._nzFileNotAuthorize.length > 0) {
82+
this.nzFilesNotAuthorize.emit(this._nzFileNotAuthorize);
83+
}
6284
}
6385

6486
e.preventDefault();
6587
}
6688

6789
onChange(e: Event): void {
90+
this._nzFileNotAuthorize = [];
6891
if (this.options.disabled) {
6992
return;
7093
}
7194
const hie = e.target as HTMLInputElement;
7295
this.uploadFiles(hie.files!);
96+
if (this._nzFileNotAuthorize.length > 0) {
97+
this.nzFilesNotAuthorize.emit(this._nzFileNotAuthorize);
98+
}
7399
hie.value = '';
74100
}
75101

76102
private traverseFileTree(files: DataTransferItemList): void {
103+
const filesToUpload: File[] = [];
77104
const _traverseFileTree = (item: NzSafeAny, path: string): void => {
78105
if (item.isFile) {
79106
item.file((file: File) => {
80107
if (this.attrAccept(file, this.options.accept)) {
81-
this.uploadFiles([file]);
108+
filesToUpload.push(file);
109+
} else {
110+
this._nzFileNotAuthorize.push({ reason: 'extension', file });
82111
}
83112
});
84113
} else if (item.isDirectory) {
@@ -95,6 +124,9 @@ export class NzUploadBtnComponent implements OnInit, OnDestroy {
95124
for (const file of files as NzSafeAny) {
96125
_traverseFileTree(file.webkitGetAsEntry(), '');
97126
}
127+
if (filesToUpload.length > 0) {
128+
this.uploadFiles(filesToUpload);
129+
}
98130
}
99131

100132
private attrAccept(file: File, acceptedFiles?: string | string[]): boolean {
@@ -135,7 +167,7 @@ export class NzUploadBtnComponent implements OnInit, OnDestroy {
135167
this.options.filters.forEach(f => {
136168
filters$ = filters$.pipe(
137169
switchMap(list => {
138-
const fnRes = f.fn(list);
170+
const fnRes = f.fn(list, this._nzFileNotAuthorize);
139171
return fnRes instanceof Observable ? fnRes : of(fnRes);
140172
})
141173
);

‎components/upload/upload.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
<ng-template #con><ng-content></ng-content></ng-template>
2424
<ng-template #btn>
2525
<div [class]="classList" [style.display]="nzShowButton ? '' : 'none'">
26-
<div nz-upload-btn #uploadComp [options]="_btnOptions!">
26+
<div nz-upload-btn #uploadComp [options]="_btnOptions!" (nzFilesNotAuthorize)="emitFilesNotAuthorize($event)">
2727
<ng-template [ngTemplateOutlet]="con"></ng-template>
2828
</div>
2929
</div>
3030
</ng-template>
3131
@if (nzType === 'drag') {
3232
<div [class]="classList" (drop)="fileDrop($event)" (dragover)="fileDrop($event)" (dragleave)="fileDrop($event)">
33-
<div nz-upload-btn #uploadComp [options]="_btnOptions!" class="ant-upload-btn">
33+
<div nz-upload-btn #uploadComp [options]="_btnOptions!" class="ant-upload-btn" (nzFilesNotAuthorize)="emitFilesNotAuthorize($event)">
3434
<div class="ant-upload-drag-container">
3535
<ng-template [ngTemplateOutlet]="con"></ng-template>
3636
</div>

‎components/upload/upload.component.ts

+29-7
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,31 @@ import { Platform } from '@angular/cdk/platform';
88
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
99
import {
1010
AfterViewInit,
11+
booleanAttribute,
1112
ChangeDetectionStrategy,
1213
ChangeDetectorRef,
1314
Component,
1415
EventEmitter,
16+
inject,
1517
Input,
18+
numberAttribute,
1619
OnChanges,
1720
OnDestroy,
1821
OnInit,
1922
Output,
2023
TemplateRef,
2124
ViewChild,
22-
ViewEncapsulation,
23-
booleanAttribute,
24-
inject,
25-
numberAttribute
25+
ViewEncapsulation
2626
} from '@angular/core';
27-
import { Observable, Subject, Subscription, of } from 'rxjs';
27+
import { Observable, of, Subject, Subscription } from 'rxjs';
2828
import { filter, takeUntil } from 'rxjs/operators';
2929

3030
import { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types';
3131
import { fromEventOutsideAngular, toBoolean } from 'ng-zorro-antd/core/util';
3232
import { NzI18nService, NzUploadI18nInterface } from 'ng-zorro-antd/i18n';
3333

3434
import {
35+
NzFileNotAuthorize,
3536
NzIconRenderTemplate,
3637
NzShowUploadList,
3738
NzUploadChangeParam,
@@ -116,6 +117,7 @@ export class NzUploadComponent implements OnInit, AfterViewInit, OnChanges, OnDe
116117

117118
@Output() readonly nzChange: EventEmitter<NzUploadChangeParam> = new EventEmitter<NzUploadChangeParam>();
118119
@Output() readonly nzFileListChange: EventEmitter<NzUploadFile[]> = new EventEmitter<NzUploadFile[]>();
120+
@Output() readonly nzFilesNotAuthorize: EventEmitter<NzFileNotAuthorize[]> = new EventEmitter<NzFileNotAuthorize[]>();
119121

120122
_btnOptions?: ZipButtonOptions;
121123

@@ -138,14 +140,30 @@ export class NzUploadComponent implements OnInit, AfterViewInit, OnChanges, OnDe
138140
if (this.nzSize > 0 && filters.findIndex(w => w.name === 'size') === -1) {
139141
filters.push({
140142
name: 'size',
141-
fn: (fileList: NzUploadFile[]) => fileList.filter(w => w.size! / 1024 <= this.nzSize)
143+
fn: (fileList: NzUploadFile[], filesNotAuthorize: NzFileNotAuthorize[]) =>
144+
fileList.filter(w => {
145+
if (w.size! / 1024 <= this.nzSize) {
146+
return true;
147+
} else {
148+
filesNotAuthorize.push({ file: w, reason: 'size' });
149+
return false;
150+
}
151+
})
142152
});
143153
}
144154
if (this.nzFileType && this.nzFileType.length > 0 && filters.findIndex(w => w.name === 'type') === -1) {
145155
const types = this.nzFileType.split(',');
146156
filters.push({
147157
name: 'type',
148-
fn: (fileList: NzUploadFile[]) => fileList.filter(w => ~types.indexOf(w.type!))
158+
fn: (fileList: NzUploadFile[], nzFileNotAuthorize: NzFileNotAuthorize[]) =>
159+
fileList.filter(w => {
160+
if (~types.indexOf(w.type!)) {
161+
return true;
162+
} else {
163+
nzFileNotAuthorize.push({ file: w, reason: 'extension' });
164+
return false;
165+
}
166+
})
149167
});
150168
}
151169
this._btnOptions = {
@@ -329,6 +347,10 @@ export class NzUploadComponent implements OnInit, AfterViewInit, OnChanges, OnDe
329347
this.cdr.detectChanges();
330348
}
331349

350+
emitFilesNotAuthorize($event: NzFileNotAuthorize[]): void {
351+
this.nzFilesNotAuthorize.emit($event);
352+
}
353+
332354
// #endregion
333355

334356
ngOnInit(): void {

‎components/upload/upload.spec.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ENTER, TAB } from '@angular/cdk/keycodes';
77
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
88
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
99
import { ApplicationRef, Component, DebugElement, Injector, TemplateRef, ViewChild } from '@angular/core';
10-
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
10+
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
1111
import { By } from '@angular/platform-browser';
1212
import { provideNoopAnimations } from '@angular/platform-browser/animations';
1313
import { Observable, Observer, of, throwError } from 'rxjs';
@@ -23,6 +23,7 @@ import { provideNzIconsTesting } from 'ng-zorro-antd/icon/testing';
2323
import { NzUploadModule } from 'ng-zorro-antd/upload/upload.module';
2424

2525
import {
26+
NzFileNotAuthorize,
2627
NzIconRenderTemplate,
2728
NzShowUploadList,
2829
NzUploadChangeParam,
@@ -68,6 +69,7 @@ const PNGBIG = { target: { files: { 0: LARGEFILE, length: 1, item: () => LARGEFI
6869

6970
class Item {
7071
children?: Item[];
72+
7173
constructor(public name: string) {}
7274
}
7375

@@ -152,6 +154,15 @@ describe('upload', () => {
152154
expect(instance._beforeUploadList.length).toBe(0);
153155
});
154156

157+
it('should send an event if extension is not correct', () => {
158+
const spy = spyOn(instance, 'nzFileNotAuthorizeChange');
159+
instance.nzFileType = 'image/png';
160+
fixture.detectChanges();
161+
pageObject.postFile(JPGSMALL.target.files);
162+
expect(spy).toHaveBeenCalled();
163+
expect(spy).toHaveBeenCalledWith([{ reason: 'extension', file: JPGSMALL.target.files[0] }]);
164+
});
165+
155166
it('should limit 1kb size', () => {
156167
instance.nzSize = 1;
157168
fixture.detectChanges();
@@ -160,6 +171,14 @@ describe('upload', () => {
160171
expect(instance._beforeUploadList.length).toBe(0);
161172
});
162173

174+
it('should send an event if size is not correct', () => {
175+
const spy = spyOn(instance, 'nzFileNotAuthorizeChange');
176+
instance.nzSize = 1;
177+
fixture.detectChanges();
178+
pageObject.postLarge();
179+
expect(spy).toHaveBeenCalled();
180+
});
181+
163182
it('should be abort when user canceled', () => {
164183
pageObject.postLarge();
165184
const req = httpMock.expectOne(instance.nzAction as string);
@@ -932,6 +951,7 @@ describe('upload', () => {
932951
addEventListener(_name: string, callback: VoidFunction): void {
933952
callback();
934953
}
954+
935955
removeEventListener(): void {}
936956

937957
set src(_: string) {}
@@ -1376,6 +1396,7 @@ describe('upload', () => {
13761396
[nzFileListRender]="nzFileListRender"
13771397
(nzFileListChange)="nzFileListChange($event)"
13781398
(nzChange)="nzChange($event)"
1399+
(nzFilesNotAuthorize)="nzFileNotAuthorizeChange($event)"
13791400
>
13801401
<button nz-button>
13811402
<nz-icon nzType="upload" />
@@ -1435,6 +1456,7 @@ class TestUploadComponent {
14351456
return true;
14361457
};
14371458
_nzChange!: NzUploadChangeParam;
1459+
_nzFileNotAuthorizeChange: NzFileNotAuthorize[] = [];
14381460

14391461
nzChange(value: NzUploadChangeParam): void {
14401462
this._nzChange = value;
@@ -1444,6 +1466,10 @@ class TestUploadComponent {
14441466
this._nzChange = value;
14451467
}
14461468

1469+
nzFileNotAuthorizeChange(value: NzFileNotAuthorize[]): void {
1470+
this._nzFileNotAuthorizeChange = value;
1471+
}
1472+
14471473
directory = false;
14481474
}
14491475

0 commit comments

Comments
 (0)
Please sign in to comment.