import * as React from 'react'; import { PureComponent } from 'react'; import isPromise from 'zent/es/utils/isPromise'; import { Notify } from 'zent'; import { I18nReceiver as Receiver, II18nLocaleUpload } from '../../i18n'; import { formatFileSize, base64ToArrayBuffer, formatErrorMessages, } from '../utils'; import fileType from '../utils/file-type'; import uploadLocalImage from './UploadLocal'; import { UID_KEY, DEFAULT_ACCEPT } from '../constants'; import { IUploadErrorMessage } from '../Upload'; const noop = res => res; export interface IUploadLocalFile { src: string; file: Blob; __uid: number; } export interface IFileInputProps { initIndex: number; maxAmount: number; silent: boolean; maxSize: number; type: string; auto?: boolean; filterFiles: (files: File[]) => File[] | Promise; onError: () => void; onChange?: (files: IUploadLocalFile[]) => void; errorMessages?: IUploadErrorMessage; i18n: any; } export default class FileInput extends PureComponent { static defaultProps = { initIndex: 0, maxAmount: 0, silent: false, maxSize: 0, type: '', filterFiles: noop, onError: noop, }; constructor(props) { super(props); let { accept, type } = props; // 根据type设置accept默认值 if (!accept) { accept = DEFAULT_ACCEPT[type]; } this.state = { accept, }; } onFileChange = localFiles => { const { onChange } = this.props; if (onChange) { onChange(localFiles); } else { uploadLocalImage(this.props, { localFiles, }); } }; processFiles = i18n => evt => { let files = Array.from(evt.target.files); const { filterFiles, onError } = this.props; const filterResult = filterFiles(files); const iterator = this.iteratorFiles(i18n); if (isPromise(filterResult)) { filterResult.then(iterator, onError); } else { files = filterResult; iterator(files); } // 清除当前的值,否则选同一张图片不会触发事件 evt.target.value = null; }; iteratorFiles = (i18n: II18nLocaleUpload) => (files: File[]) => { const { type, maxSize, maxAmount, silent, initIndex, errorMessages, } = this.props; files && files.forEach((file, index) => { if (maxAmount && index + initIndex >= maxAmount) { const message = formatErrorMessages( errorMessages.overMaxAmount, { maxAmount, type }, i18n.input.maxAmount ); !silent && message && Notify.error(message); return false; } if (!maxSize || file.size <= maxSize) { this.addFile(file, index, i18n); } else { const message = formatErrorMessages( errorMessages.overMaxSize, { maxSize: formatFileSize(maxSize), type, }, i18n.input.maxSize ); !silent && message && Notify.error(message); } return true; }); }; addFile(file, index, i18n) { const fileReader = new FileReader(); const { type, initIndex, silent, errorMessages } = this.props; const { accept } = this.state; const localFiles = []; fileReader.onload = e => { const mimeType = fileType( base64ToArrayBuffer( (e.target as any).result.replace(/^(.*?)base64,/, '') ) ); if ( accept && (!mimeType || mimeType.mime.match(new RegExp(accept.replace(/, ?/g, '|')))) ) { localFiles.push({ src: (e.target as any).result, file, [UID_KEY]: initIndex + index, }); } else { const message = formatErrorMessages( errorMessages.wrongMimeType, { type }, i18n.input.type ); !silent && message && Notify.error(message); } this.onFileChange(localFiles); }; fileReader.readAsDataURL(file); } autoShowInput = fileInput => { const { maxAmount, auto } = this.props; if ( auto && maxAmount === 1 && fileInput && typeof fileInput.click === 'function' ) { fileInput.click(); } }; render() { const { maxAmount } = this.props; const { accept } = this.state; return ( {(i18n: II18nLocaleUpload) => ( )} ); } }