mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-28 05:05:48 +08:00
feat: Upload support customize previewFile (#15984)
* support preview file * use promise * dealy load * use canvas of render * use domHook of test * update demo * add snapshot * update types * update testcase
This commit is contained in:
parent
574313d2d8
commit
c4283ebe72
54
components/__tests__/util/domHook.js
Normal file
54
components/__tests__/util/domHook.js
Normal file
@ -0,0 +1,54 @@
|
||||
export function spyElementPrototypes(Element, properties) {
|
||||
const propNames = Object.keys(properties);
|
||||
const originDescriptors = {};
|
||||
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
|
||||
originDescriptors[propName] = originDescriptor;
|
||||
|
||||
const spyProp = properties[propName];
|
||||
|
||||
if (typeof spyProp === 'function') {
|
||||
// If is a function
|
||||
Element.prototype[propName] = function spyFunc(...args) {
|
||||
return spyProp.call(this, originDescriptor, ...args);
|
||||
};
|
||||
} else {
|
||||
// Otherwise tread as a property
|
||||
Object.defineProperty(Element.prototype, propName, {
|
||||
...spyProp,
|
||||
set(value) {
|
||||
if (spyProp.set) {
|
||||
return spyProp.set.call(this, originDescriptor, value);
|
||||
}
|
||||
return originDescriptor.set(value);
|
||||
},
|
||||
get() {
|
||||
if (spyProp.get) {
|
||||
return spyProp.get.call(this, originDescriptor);
|
||||
}
|
||||
return originDescriptor.get();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
mockRestore() {
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = originDescriptors[propName];
|
||||
if (typeof originDescriptor === 'function') {
|
||||
Element.prototype[propName] = originDescriptor;
|
||||
} else {
|
||||
Object.defineProperty(Element.prototype, propName, originDescriptor);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function spyElementPrototype(Element, propName, property) {
|
||||
return spyElementPrototypes(Element, {
|
||||
[propName]: property,
|
||||
});
|
||||
}
|
@ -238,12 +238,13 @@ class Upload extends React.Component<UploadProps, UploadState> {
|
||||
};
|
||||
|
||||
renderUploadList = (locale: UploadLocale) => {
|
||||
const { showUploadList, listType, onPreview } = this.props;
|
||||
const { showUploadList, listType, onPreview, previewFile } = this.props;
|
||||
const { showRemoveIcon, showPreviewIcon } = showUploadList as any;
|
||||
return (
|
||||
<UploadList
|
||||
listType={listType}
|
||||
items={this.state.fileList}
|
||||
previewFile={previewFile}
|
||||
onPreview={onPreview}
|
||||
onRemove={this.handleManualRemove}
|
||||
showRemoveIcon={showRemoveIcon}
|
||||
|
@ -2,39 +2,12 @@ import * as React from 'react';
|
||||
import Animate from 'rc-animate';
|
||||
import classNames from 'classnames';
|
||||
import { UploadListProps, UploadFile, UploadListType } from './interface';
|
||||
import { previewImage, isImageUrl } from './utils';
|
||||
import Icon from '../icon';
|
||||
import Tooltip from '../tooltip';
|
||||
import Progress from '../progress';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
|
||||
const extname = (url: string) => {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
const temp = url.split('/');
|
||||
const filename = temp[temp.length - 1];
|
||||
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
|
||||
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
|
||||
};
|
||||
const isImageFileType = (type: string): boolean => !!type && type.indexOf('image/') === 0;
|
||||
const isImageUrl = (file: UploadFile): boolean => {
|
||||
if (isImageFileType(file.type)) {
|
||||
return true;
|
||||
}
|
||||
const url: string = (file.thumbUrl || file.url) as string;
|
||||
const extension = extname(url);
|
||||
if (/^data:image\//.test(url) || /(webp|svg|png|gif|jpg|jpeg|bmp|dpg)$/i.test(extension)) {
|
||||
return true;
|
||||
} else if (/^data:/.test(url)) {
|
||||
// other file types of base64
|
||||
return false;
|
||||
} else if (extension) {
|
||||
// other file types which have extension
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export default class UploadList extends React.Component<UploadListProps, any> {
|
||||
static defaultProps = {
|
||||
listType: 'text' as UploadListType, // or picture
|
||||
@ -44,6 +17,7 @@ export default class UploadList extends React.Component<UploadListProps, any> {
|
||||
},
|
||||
showRemoveIcon: true,
|
||||
showPreviewIcon: true,
|
||||
previewFile: previewImage,
|
||||
};
|
||||
|
||||
handleClose = (file: UploadFile) => {
|
||||
@ -62,21 +36,12 @@ export default class UploadList extends React.Component<UploadListProps, any> {
|
||||
return onPreview(file);
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
|
||||
previewFile = (file: File | Blob, callback: Function) => {
|
||||
if (!isImageFileType(file.type)) {
|
||||
return callback('');
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => callback(reader.result);
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.listType !== 'picture' && this.props.listType !== 'picture-card') {
|
||||
const { listType, items, previewFile } = this.props;
|
||||
if (listType !== 'picture' && listType !== 'picture-card') {
|
||||
return;
|
||||
}
|
||||
(this.props.items || []).forEach(file => {
|
||||
(items || []).forEach(file => {
|
||||
if (
|
||||
typeof document === 'undefined' ||
|
||||
typeof window === 'undefined' ||
|
||||
@ -88,10 +53,13 @@ export default class UploadList extends React.Component<UploadListProps, any> {
|
||||
return;
|
||||
}
|
||||
file.thumbUrl = '';
|
||||
this.previewFile(file.originFileObj, (previewDataUrl: string) => {
|
||||
file.thumbUrl = previewDataUrl;
|
||||
this.forceUpdate();
|
||||
});
|
||||
if (previewFile) {
|
||||
previewFile(file.originFileObj).then((previewDataUrl: string) => {
|
||||
// Need append '' to avoid dead loop
|
||||
file.thumbUrl = previewDataUrl || '';
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -631,6 +631,21 @@ exports[`renders ./components/upload/demo/picture-style.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/preview-file.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-upload ant-upload-select ant-upload-select-picture"
|
||||
/>
|
||||
<div
|
||||
class="ant-upload-list ant-upload-list-picture"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/upload/demo/upload-manually.md correctly 1`] = `
|
||||
<div>
|
||||
<span
|
||||
|
@ -3,6 +3,7 @@ import { mount } from 'enzyme';
|
||||
import Upload from '..';
|
||||
import UploadList from '../UploadList';
|
||||
import Form from '../../form';
|
||||
import { spyElementPrototypes } from '../../__tests__/util/domHook';
|
||||
import { errorRequest, successRequest } from './requests';
|
||||
import { setup, teardown } from './mock';
|
||||
|
||||
@ -26,8 +27,58 @@ const fileList = [
|
||||
];
|
||||
|
||||
describe('Upload List', () => {
|
||||
// jsdom not support `createObjectURL` yet. Let's handle this.
|
||||
const originCreateObjectURL = window.URL.createObjectURL;
|
||||
window.URL.createObjectURL = jest.fn(() => '');
|
||||
|
||||
// Mock dom
|
||||
let size = { width: 0, height: 0 };
|
||||
function setSize(width, height) {
|
||||
size = { width, height };
|
||||
}
|
||||
const imageSpy = spyElementPrototypes(Image, {
|
||||
src: {
|
||||
set() {
|
||||
if (this.onload) {
|
||||
this.onload();
|
||||
}
|
||||
},
|
||||
},
|
||||
width: {
|
||||
get: () => size.width,
|
||||
},
|
||||
height: {
|
||||
get: () => size.height,
|
||||
},
|
||||
});
|
||||
|
||||
let drawImageCallback = null;
|
||||
function hookDrawImageCall(callback) {
|
||||
drawImageCallback = callback;
|
||||
}
|
||||
const canvasSpy = spyElementPrototypes(HTMLCanvasElement, {
|
||||
getContext: () => ({
|
||||
drawImage: (...args) => {
|
||||
if (drawImageCallback) drawImageCallback(...args);
|
||||
},
|
||||
}),
|
||||
|
||||
toDataURL: () => 'data:image/png;base64,',
|
||||
});
|
||||
|
||||
// HTMLCanvasElement.prototype
|
||||
|
||||
beforeEach(() => setup());
|
||||
afterEach(() => teardown());
|
||||
afterEach(() => {
|
||||
teardown();
|
||||
drawImageCallback = null;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
window.URL.createObjectURL = originCreateObjectURL;
|
||||
imageSpy.mockRestore();
|
||||
canvasSpy.mockRestore();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/4653
|
||||
it('should use file.thumbUrl for <img /> in priority', () => {
|
||||
@ -198,20 +249,45 @@ describe('Upload List', () => {
|
||||
expect(handleChange.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should generate thumbUrl from file', async () => {
|
||||
const handlePreview = jest.fn();
|
||||
const newFileList = [...fileList];
|
||||
const newFile = { ...fileList[0], uid: '-3', originFileObj: new File([], 'xxx.png') };
|
||||
delete newFile.thumbUrl;
|
||||
newFileList.push(newFile);
|
||||
const wrapper = mount(
|
||||
<Upload listType="picture-card" defaultFileList={newFileList} onPreview={handlePreview}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
wrapper.setState({});
|
||||
await delay(0);
|
||||
expect(wrapper.state().fileList[2].thumbUrl).not.toBe(undefined);
|
||||
describe('should generate thumbUrl from file', () => {
|
||||
[
|
||||
{ width: 100, height: 200, name: 'height large than width' },
|
||||
{ width: 200, height: 100, name: 'width large than height' },
|
||||
].forEach(({ width, height, name }) => {
|
||||
it(name, async () => {
|
||||
setSize(width, height);
|
||||
const onDrawImage = jest.fn();
|
||||
hookDrawImageCall(onDrawImage);
|
||||
|
||||
const handlePreview = jest.fn();
|
||||
const newFileList = [...fileList];
|
||||
const newFile = {
|
||||
...fileList[0],
|
||||
uid: '-3',
|
||||
originFileObj: new File([], 'xxx.png', { type: 'image/png' }),
|
||||
};
|
||||
delete newFile.thumbUrl;
|
||||
newFileList.push(newFile);
|
||||
const wrapper = mount(
|
||||
<Upload listType="picture-card" defaultFileList={newFileList} onPreview={handlePreview}>
|
||||
<button type="button">upload</button>
|
||||
</Upload>,
|
||||
);
|
||||
wrapper.setState({});
|
||||
await delay(0);
|
||||
|
||||
expect(wrapper.state().fileList[2].thumbUrl).not.toBe(undefined);
|
||||
expect(onDrawImage).toHaveBeenCalled();
|
||||
|
||||
// Offset check
|
||||
const [, offsetX, offsetY] = onDrawImage.mock.calls[0];
|
||||
if (width > height) {
|
||||
expect(offsetX < 0).toBeTruthy();
|
||||
} else {
|
||||
expect(offsetY < 0).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should non-image format file preview', () => {
|
||||
@ -356,15 +432,13 @@ describe('Upload List', () => {
|
||||
expect(wrapper.handlePreview()).toBe(undefined);
|
||||
});
|
||||
|
||||
it('previewFile should work correctly', () => {
|
||||
const callback = jest.fn();
|
||||
it('previewFile should work correctly', async () => {
|
||||
const file = new File([''], 'test.txt', { type: 'text/plain' });
|
||||
const items = [{ uid: 'upload-list-item', url: '' }];
|
||||
const wrapper = mount(
|
||||
<UploadList listType="picture-card" items={items} locale={{ previewFile: '' }} />,
|
||||
).instance();
|
||||
wrapper.previewFile(file, callback);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
return wrapper.props.previewFile(file);
|
||||
});
|
||||
|
||||
it('extname should work correctly when url not exists', () => {
|
||||
@ -404,7 +478,7 @@ describe('Upload List', () => {
|
||||
expect(onPreview).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('upload image file should be converted to the base64', done => {
|
||||
it('upload image file should be converted to the base64', async () => {
|
||||
const mockFile = new File([''], 'foo.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
@ -413,14 +487,12 @@ describe('Upload List', () => {
|
||||
<UploadList listType="picture-card" items={fileList} locale={{ uploading: 'uploading' }} />,
|
||||
);
|
||||
const instance = wrapper.instance();
|
||||
const callback = dataUrl => {
|
||||
return instance.props.previewFile(mockFile).then(dataUrl => {
|
||||
expect(dataUrl).toEqual('data:image/png;base64,');
|
||||
done();
|
||||
};
|
||||
instance.previewFile(mockFile, callback);
|
||||
});
|
||||
});
|
||||
|
||||
it("upload non image file shouldn't be converted to the base64", () => {
|
||||
it("upload non image file shouldn't be converted to the base64", async () => {
|
||||
const mockFile = new File([''], 'foo.7z', {
|
||||
type: 'application/x-7z-compressed',
|
||||
});
|
||||
@ -429,8 +501,33 @@ describe('Upload List', () => {
|
||||
<UploadList listType="picture-card" items={fileList} locale={{ uploading: 'uploading' }} />,
|
||||
);
|
||||
const instance = wrapper.instance();
|
||||
const callback = jest.fn();
|
||||
instance.previewFile(mockFile, callback);
|
||||
expect(callback).toHaveBeenCalledWith('');
|
||||
return instance.props.previewFile(mockFile).then(dataUrl => {
|
||||
expect(dataUrl).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
it('customize previewFile support', async () => {
|
||||
const mockThumbnail = 'mock-image';
|
||||
const previewFile = jest.fn(() => {
|
||||
return Promise.resolve(mockThumbnail);
|
||||
});
|
||||
const file = {
|
||||
...fileList[0],
|
||||
originFileObj: new File([], 'xxx.png'),
|
||||
};
|
||||
delete file.thumbUrl;
|
||||
|
||||
const wrapper = mount(
|
||||
<Upload listType="picture" defaultFileList={[file]} previewFile={previewFile}>
|
||||
<button type="button" />
|
||||
</Upload>,
|
||||
);
|
||||
wrapper.setState({});
|
||||
await delay(0);
|
||||
|
||||
expect(previewFile).toHaveBeenCalledWith(file.originFileObj);
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('.ant-upload-list-item-thumbnail img').prop('src')).toBe(mockThumbnail);
|
||||
});
|
||||
});
|
||||
|
44
components/upload/demo/preview-file.md
Normal file
44
components/upload/demo/preview-file.md
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
order: 9
|
||||
title:
|
||||
zh-CN: 自定义预览
|
||||
en-US: Customize preview file
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
自定义本地预览,用于处理非图片格式文件(例如视频文件)。
|
||||
|
||||
## en-US
|
||||
|
||||
Customize local preview. Can handle with non-image format files such as video.
|
||||
|
||||
````jsx
|
||||
import { Upload, Button, Icon } from 'antd';
|
||||
|
||||
const props = {
|
||||
action: '//jsonplaceholder.typicode.com/posts/',
|
||||
listType: 'picture',
|
||||
previewFile(file) {
|
||||
console.log('Your upload file:', file);
|
||||
// Your process logic. Here we just mock to the same file
|
||||
return fetch('https://next.json-generator.com/api/json/get/4ytyBoLK8', {
|
||||
method: 'POST',
|
||||
body: file,
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(({ thumbnail }) => thumbnail);
|
||||
},
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<Upload {...props}>
|
||||
<Button>
|
||||
<Icon type="upload" /> Upload
|
||||
</Button>
|
||||
</Upload>
|
||||
</div>,
|
||||
mountNode
|
||||
);
|
||||
````
|
@ -16,28 +16,29 @@ Uploading is the process of publishing information (web pages, text, pictures, v
|
||||
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - |
|
||||
| action | Uploading URL | string\|(file) => `Promise` | - |
|
||||
| directory | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false |
|
||||
| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. **Warning:this function is not supported in IE9**。 | (file, fileList) => `boolean | Promise` | - |
|
||||
| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | Function | - |
|
||||
| data | Uploading params or function which can return uploading params. | object\|function(file) | - |
|
||||
| defaultFileList | Default list of files that have been uploaded. | object\[] | - |
|
||||
| disabled | disable upload button | boolean | false |
|
||||
| fileList | List of files that have been uploaded (controlled). Here is a common issue [#2423](https://github.com/ant-design/ant-design/issues/2423) when using it | object\[] | - |
|
||||
| headers | Set request headers, valid above IE10. | object | - |
|
||||
| listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | 'text' |
|
||||
| multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false |
|
||||
| name | The name of uploading file | string | 'file' |
|
||||
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon` and `showRemoveIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true |
|
||||
| supportServerRender | Need to be turned on while the server side is rendering | boolean | false |
|
||||
| withCredentials | ajax upload with cookie sent | boolean | false |
|
||||
| openFileDialogOnClick | click open file dialog | boolean | true |
|
||||
| onChange | A callback function, can be executed when uploading state is changing, see [onChange](#onChange) | Function | - |
|
||||
| onPreview | A callback function, will be executed when file link or preview icon is clicked | Function(file) | - |
|
||||
| onRemove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is `false` or a Promise which resolve(false) or reject | Function(file): `boolean | Promise` | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| -------- | ----------- | ---- | ------- | ------- |
|
||||
| accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - | |
|
||||
| action | Uploading URL | string\|(file) => `Promise` | - | |
|
||||
| directory | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | |
|
||||
| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. **Warning:this function is not supported in IE9**。 | (file, fileList) => `boolean | Promise` | - | |
|
||||
| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | Function | - | |
|
||||
| data | Uploading params or function which can return uploading params. | object\|function(file) | - | |
|
||||
| defaultFileList | Default list of files that have been uploaded. | object\[] | - | |
|
||||
| disabled | disable upload button | boolean | false | |
|
||||
| fileList | List of files that have been uploaded (controlled). Here is a common issue [#2423](https://github.com/ant-design/ant-design/issues/2423) when using it | object\[] | - | |
|
||||
| headers | Set request headers, valid above IE10. | object | - | |
|
||||
| listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | 'text' | |
|
||||
| multiple | Whether to support selected multiple file. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false | |
|
||||
| name | The name of uploading file | string | 'file' | |
|
||||
| previewFile | Customize preview file logic | (file: File \| Blob) => Promise<dataURL: string> | - | 3.17.0 |
|
||||
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon` and `showRemoveIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
|
||||
| supportServerRender | Need to be turned on while the server side is rendering | boolean | false | |
|
||||
| withCredentials | ajax upload with cookie sent | boolean | false | |
|
||||
| openFileDialogOnClick | click open file dialog | boolean | true | |
|
||||
| onChange | A callback function, can be executed when uploading state is changing, see [onChange](#onChange) | Function | - | |
|
||||
| onPreview | A callback function, will be executed when file link or preview icon is clicked | Function(file) | - | |
|
||||
| onRemove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is `false` or a Promise which resolve(false) or reject | Function(file): `boolean | Promise` | - | |
|
||||
|
||||
### onChange
|
||||
|
||||
|
@ -17,28 +17,29 @@ title: Upload
|
||||
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | 无 |
|
||||
| action | 上传的地址 | string\|(file) => `Promise` | 无 |
|
||||
| directory | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory))| boolean | false |
|
||||
| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象)。**注意:IE9 不支持该方法**。 | (file, fileList) => `boolean | Promise` | 无 |
|
||||
| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | Function | 无 |
|
||||
| data | 上传所需参数或返回上传参数的方法 | object\|(file) => object | 无 |
|
||||
| defaultFileList | 默认已经上传的文件列表 | object\[] | 无 |
|
||||
| disabled | 是否禁用 | boolean | false |
|
||||
| fileList | 已经上传的文件列表(受控),使用此参数时,如果遇到 `onChange` 只调用一次的问题,请参考 [#2423](https://github.com/ant-design/ant-design/issues/2423) | object\[] | 无 |
|
||||
| headers | 设置上传的请求头部,IE10 以上有效 | object | 无 |
|
||||
| listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | 'text' |
|
||||
| multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件 | boolean | false |
|
||||
| name | 发到后台的文件参数名 | string | 'file' |
|
||||
| showUploadList | 是否展示文件列表, 可设为一个对象,用于单独设定 `showPreviewIcon` 和 `showRemoveIcon` | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true |
|
||||
| supportServerRender | 服务端渲染时需要打开这个 | boolean | false |
|
||||
| withCredentials | 上传请求时是否携带 cookie | boolean | false |
|
||||
| openFileDialogOnClick | 点击打开文件对话框 | boolean | true |
|
||||
| onChange | 上传文件改变时的状态,详见 [onChange](#onChange) | Function | 无 |
|
||||
| onPreview | 点击文件链接或预览图标时的回调 | Function(file) | 无 |
|
||||
| onRemove | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象,Promise 对象 resolve(false) 或 reject 时不移除。 | Function(file): `boolean | Promise` | 无 |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | 无 | |
|
||||
| action | 上传的地址 | string\|(file) => `Promise` | 无 | |
|
||||
| directory | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory))| boolean | false | |
|
||||
| beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象)。**注意:IE9 不支持该方法**。 | (file, fileList) => `boolean | Promise` | 无 | |
|
||||
| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | Function | 无 | |
|
||||
| data | 上传所需参数或返回上传参数的方法 | object\|(file) => object | 无 | |
|
||||
| defaultFileList | 默认已经上传的文件列表 | object\[] | 无 | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| fileList | 已经上传的文件列表(受控),使用此参数时,如果遇到 `onChange` 只调用一次的问题,请参考 [#2423](https://github.com/ant-design/ant-design/issues/2423) | object\[] | 无 | |
|
||||
| headers | 设置上传的请求头部,IE10 以上有效 | object | 无 | |
|
||||
| listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | 'text' | |
|
||||
| multiple | 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件 | boolean | false | |
|
||||
| name | 发到后台的文件参数名 | string | 'file' | |
|
||||
| previewFile | 自定义文件预览逻辑 | (file: File \| Blob) => Promise<dataURL: string> | 无 | 3.17.0 |
|
||||
| showUploadList | 是否展示文件列表, 可设为一个对象,用于单独设定 `showPreviewIcon` 和 `showRemoveIcon` | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true | |
|
||||
| supportServerRender | 服务端渲染时需要打开这个 | boolean | false | |
|
||||
| withCredentials | 上传请求时是否携带 cookie | boolean | false | |
|
||||
| openFileDialogOnClick | 点击打开文件对话框 | boolean | true | |
|
||||
| onChange | 上传文件改变时的状态,详见 [onChange](#onChange) | Function | 无 | |
|
||||
| onPreview | 点击文件链接或预览图标时的回调 | Function(file) | 无 | |
|
||||
| onRemove | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象,Promise 对象 resolve(false) 或 reject 时不移除。 | Function(file): `boolean | Promise` | 无 | |
|
||||
|
||||
### onChange
|
||||
|
||||
|
@ -50,6 +50,8 @@ export interface UploadLocale {
|
||||
export type UploadType = 'drag' | 'select';
|
||||
export type UploadListType = 'text' | 'picture' | 'picture-card';
|
||||
|
||||
type PreviewFileHandler = (file: File | Blob) => PromiseLike<string>;
|
||||
|
||||
export interface UploadProps {
|
||||
type?: UploadType;
|
||||
name?: string;
|
||||
@ -77,6 +79,7 @@ export interface UploadProps {
|
||||
openFileDialogOnClick?: boolean;
|
||||
locale?: UploadLocale;
|
||||
id?: string;
|
||||
previewFile?: PreviewFileHandler;
|
||||
}
|
||||
|
||||
export interface UploadState {
|
||||
@ -94,4 +97,5 @@ export interface UploadListProps {
|
||||
showRemoveIcon?: boolean;
|
||||
showPreviewIcon?: boolean;
|
||||
locale: UploadLocale;
|
||||
previewFile?: PreviewFileHandler;
|
||||
}
|
||||
|
@ -56,3 +56,73 @@ export function removeFileItem(file: UploadFile, fileList: UploadFile[]) {
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
// ==================== Default Image Preview ====================
|
||||
const extname = (url: string) => {
|
||||
if (!url) {
|
||||
return '';
|
||||
}
|
||||
const temp = url.split('/');
|
||||
const filename = temp[temp.length - 1];
|
||||
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
|
||||
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
|
||||
};
|
||||
const isImageFileType = (type: string): boolean => !!type && type.indexOf('image/') === 0;
|
||||
export const isImageUrl = (file: UploadFile): boolean => {
|
||||
if (isImageFileType(file.type)) {
|
||||
return true;
|
||||
}
|
||||
const url: string = (file.thumbUrl || file.url) as string;
|
||||
const extension = extname(url);
|
||||
if (/^data:image\//.test(url) || /(webp|svg|png|gif|jpg|jpeg|bmp|dpg)$/i.test(extension)) {
|
||||
return true;
|
||||
} else if (/^data:/.test(url)) {
|
||||
// other file types of base64
|
||||
return false;
|
||||
} else if (extension) {
|
||||
// other file types which have extension
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const MEASURE_SIZE = 200;
|
||||
export function previewImage(file: File | Blob): Promise<string> {
|
||||
return new Promise(resolve => {
|
||||
if (!isImageFileType(file.type)) {
|
||||
resolve('');
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = MEASURE_SIZE;
|
||||
canvas.height = MEASURE_SIZE;
|
||||
canvas.style.cssText = `position: fixed; left: 0; top: 0; width: ${MEASURE_SIZE}px; height: ${MEASURE_SIZE}px; z-index: 9999; display: none;`;
|
||||
document.body.appendChild(canvas);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
const { width, height } = img;
|
||||
|
||||
let drawWidth = MEASURE_SIZE;
|
||||
let drawHeight = MEASURE_SIZE;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
if (width < height) {
|
||||
drawHeight = height * (MEASURE_SIZE / width);
|
||||
offsetY = -(drawHeight - drawWidth) / 2;
|
||||
} else {
|
||||
drawWidth = width * (MEASURE_SIZE / height);
|
||||
offsetX = -(drawWidth - drawHeight) / 2;
|
||||
}
|
||||
|
||||
ctx!.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
||||
const dataURL = canvas.toDataURL();
|
||||
document.body.removeChild(canvas);
|
||||
|
||||
resolve(dataURL);
|
||||
};
|
||||
img.src = window.URL.createObjectURL(file);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user