Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(module:upload): add directory support #2164

Merged
merged 20 commits into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions components/upload/demo/custom-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 10
title:
zh-CN: 自定义上传
en-US: Custom request
---

## zh-CN

使用 `nzCustomRequest` 改变上传行为。

## en-US

Use `nzCustomRequest` override for the default xhr behavior.
80 changes: 80 additions & 0 deletions components/upload/demo/custom-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Component } from '@angular/core';
import { HttpRequest, HttpClient, HttpEventType, HttpEvent, HttpResponse } from '@angular/common/http';
import { NzMessageService, UploadXHRArgs } from 'ng-zorro-antd';
import { forkJoin } from 'rxjs';

@Component({
selector: 'nz-demo-upload-custom-request',
template: `
<nz-upload
nzAction="https://jsonplaceholder.typicode.com/posts/"
[nzCustomRequest]="customReq">
<button nz-button>
<i class="anticon anticon-upload"></i><span>Click to Upload</span>
</button>
</nz-upload>
`
})
export class NzDemoUploadCustomRequestComponent {

constructor(private http: HttpClient, private msg: NzMessageService) {}

customReq = (item: UploadXHRArgs) => {
// 构建一个 FormData 对象,用于存储文件或其他参数
const formData = new FormData();
// tslint:disable-next-line:no-any
formData.append('file', item.file as any);
formData.append('id', '1000');
const req = new HttpRequest('POST', item.action, formData, {
reportProgress : true,
withCredentials: true
});
// 始终返回一个 `Subscription` 对象,nz-upload 会在适当时机自动取消订阅
return this.http.request(req).subscribe((event: HttpEvent<{}>) => {
if (event.type === HttpEventType.UploadProgress) {
if (event.total > 0) {
// tslint:disable-next-line:no-any
(event as any).percent = event.loaded / event.total * 100;
}
// 处理上传进度条,必须指定 `percent` 属性来表示进度
item.onProgress(event, item.file);
} else if (event instanceof HttpResponse) {
// 处理成功
item.onSuccess(event.body, item.file, event);
}
}, (err) => {
// 处理失败
item.onError(err, item.file);
});
}

// 一个简单的分片上传
customBigReq = (item: UploadXHRArgs) => {
const size = item.file.size;
const chunkSize = parseInt((size / 3) + '', 10);
const maxChunk = Math.ceil(size / chunkSize);
const reqs = Array(maxChunk).fill(0).map((v: {}, index: number) => {
const start = index * chunkSize;
let end = start + chunkSize;
if (size - end < 0) {
end = size;
}
const formData = new FormData();
formData.append('file', item.file.slice(start, end));
formData.append('start', start.toString());
formData.append('end', end.toString());
formData.append('index', index.toString());
const req = new HttpRequest('POST', item.action, formData, {
withCredentials: true
});
return this.http.request(req);
});
return forkJoin(...reqs).subscribe(resules => {
// 处理成功
item.onSuccess({}, item.file, event);
}, (err) => {
// 处理失败
item.onError(err, item.file);
});
}
}
14 changes: 14 additions & 0 deletions components/upload/demo/directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 6
title:
zh-CN: 文件夹上传
en-US: Upload directory
---

## zh-CN

支持上传一个文件夹里的所有文件。

## en-US

You can select and upload a whole directory.
15 changes: 15 additions & 0 deletions components/upload/demo/directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-upload-directory',
template: `
<nz-upload
nzAction="https://jsonplaceholder.typicode.com/posts/"
nzDirectory>
<button nz-button>
<i class="anticon anticon-upload"></i> Upload Directory
</button>
</nz-upload>
`
})
export class NzDemoUploadDirectoryComponent {}
2 changes: 1 addition & 1 deletion components/upload/demo/picture-style.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 6
order: 8
title:
zh-CN: 图片列表样式
en-US: Pictures with list style
Expand Down
1 change: 1 addition & 0 deletions components/upload/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v
| --- | --- | --- | --- |
| `[nzAccept]` | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept) | string | - |
| `[nzAction]` | Required. Uploading URL | string | - |
| `[nzDirectory]` | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false |
| `[nzBeforeUpload]` | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a Observable. **Warning:this function is not supported in IE9**. NOTICE: Muse be use `=>` to define the method. | (file, fileList) => `boolean|Observable` | - |
| `[nzCustomRequest]` | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest. NOTICE: Muse be use `=>` to define the method. | `(item) => Subscription` | - |
| `[nzData]` | Uploading params or function which can return uploading params. NOTICE: Muse be use `=>` to define the method. | `Object|((file: UploadFile) => Object)` | - |
Expand Down
1 change: 1 addition & 0 deletions components/upload/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ title: Upload
| --- | --- | --- | --- |
| `[nzAccept]` | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept) | string | - |
| `[nzAction]` | 必选参数, 上传的地址 | string | - |
| `[nzDirectory]` | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false |
| `[nzBeforeUpload]` | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。注意:**IE9** 不支持该方法;注意:务必使用 `=>` 定义处理方法。 | (file, fileList) => `boolean|Observable` | - |
| `[nzCustomRequest]` | 通过覆盖默认的上传行为,可以自定义自己的上传实现;注意:务必使用 `=>` 定义处理方法。 | `(item) => Subscription` | - |
| `[nzData]` | 上传所需参数或返回上传参数的方法;注意:务必使用 `=>` 定义处理方法。 | `Object|((file: UploadFile) => Object)` | - |
Expand Down
1 change: 1 addition & 0 deletions components/upload/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ZipButtonOptions {
disabled?: boolean;
accept?: string | string[];
action?: string;
directory?: boolean;
beforeUpload?: (file: UploadFile, fileList: UploadFile[]) => boolean | Observable<any>;
customRequest?: (item: any) => Subscription;
data?: {} | ((file: UploadFile) => {});
Expand Down
5 changes: 4 additions & 1 deletion components/upload/nz-upload-btn.component.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<input type="file" #file (change)="onChange($event)"
[attr.accept]="options.accept" [multiple]="options.multiple" style="display: none;">
[attr.accept]="options.accept"
[attr.directory]="options.directory ? 'directory': null"
[attr.webkitdirectory]="options.directory ? 'webkitdirectory': null"
[multiple]="options.multiple" style="display: none;">
<ng-content></ng-content>
41 changes: 35 additions & 6 deletions components/upload/nz-upload-btn.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,15 @@ export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy {
e.preventDefault();
return;
}
const files: File[] = Array.prototype.slice.call(e.dataTransfer.files).filter(
(file: File) => this.attrAccept(file, this.options.accept)
);
if (files.length) {
this.uploadFiles(files);
if (this.options.directory) {
this.traverseFileTree(e.dataTransfer.items);
} else {
const files: File[] = Array.prototype.slice.call(e.dataTransfer.files).filter(
(file: File) => this.attrAccept(file, this.options.accept)
);
if (files.length) {
this.uploadFiles(files);
}
}

e.preventDefault();
Expand All @@ -85,6 +89,31 @@ export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy {
hie.value = '';
}

// tslint:disable-next-line:no-any
private traverseFileTree(files: any): void {
// tslint:disable-next-line:no-any
const _traverseFileTree = (item: any, path: string) => {
if (item.isFile) {
item.file((file) => {
if (this.attrAccept(file, this.options.accept)) {
this.uploadFiles([file]);
}
});
} else if (item.isDirectory) {
const dirReader = item.createReader();

dirReader.readEntries((entries) => {
for (const entrieItem of entries) {
_traverseFileTree(entrieItem, `${path}${item.name}/`);
}
});
}
};
for (const file of files) {
_traverseFileTree(file.webkitGetAsEntry(), '');
}
}

private attrAccept(file: File, acceptedFiles: string | string[]): boolean {
if (file && acceptedFiles) {
const acceptedFilesArray = Array.isArray(acceptedFiles) ? acceptedFiles : acceptedFiles.split(',');
Expand Down Expand Up @@ -133,7 +162,7 @@ export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy {
if (processedFileType === '[object File]' || processedFileType === '[object Blob]') {
this.attachUid(processedFile);
this.post(processedFile);
} else {
} else if (typeof processedFile === 'boolean' && processedFile !== false) {
this.post(file);
}
});
Expand Down
9 changes: 4 additions & 5 deletions components/upload/nz-upload.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { of, Observable, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';

import { toBoolean, toNumber } from '../core/util/convert';
import { toBoolean, toNumber, InputBoolean } from '../core/util/convert';
import { NzI18nService } from '../i18n/nz-i18n.service';

import {
Expand All @@ -39,8 +39,6 @@ import { NzUploadBtnComponent } from './nz-upload-btn.component';
export class NzUploadComponent implements OnInit, OnChanges, OnDestroy {
private i18n$: Subscription;
locale: any = {};
private inited = false;
private progressTimer: any;
@ViewChild('upload') upload: NzUploadBtnComponent;

// region: fields
Expand Down Expand Up @@ -70,6 +68,7 @@ export class NzUploadComponent implements OnInit, OnChanges, OnDestroy {
@Input() nzFileType: string;
@Input() nzAccept: string | string[];
@Input() nzAction: string;
@Input() @InputBoolean() nzDirectory: boolean = false;
@Input() nzBeforeUpload: (file: UploadFile, fileList: UploadFile[]) => boolean | Observable<any>;
@Input() nzCustomRequest: (item: any) => Subscription;
@Input() nzData: {} | ((file: UploadFile) => {});
Expand Down Expand Up @@ -176,6 +175,7 @@ export class NzUploadComponent implements OnInit, OnChanges, OnDestroy {
disabled : this.nzDisabled,
accept : this.nzAccept,
action : this.nzAction,
directory : this.nzDirectory,
beforeUpload : this.nzBeforeUpload,
customRequest : this.nzCustomRequest,
data : this.nzData,
Expand Down Expand Up @@ -240,7 +240,7 @@ export class NzUploadComponent implements OnInit, OnChanges, OnDestroy {
file.thumbUrl = '';

const reader = new FileReader();
reader.onloadend = () => file.thumbUrl = reader.result;
reader.onloadend = () => file.thumbUrl = reader.result as string;
reader.readAsDataURL(file.originFileObj);
}

Expand Down Expand Up @@ -361,7 +361,6 @@ export class NzUploadComponent implements OnInit, OnChanges, OnDestroy {

// endregion
ngOnInit(): void {
this.inited = true;
this.i18n$ = this.i18n.localeChange.subscribe(() => {
this.locale = this.i18n.getLocaleData('Upload');
this.cd.detectChanges();
Expand Down
Loading