UNPKG

@boewa-software/react-async-uploader

Version:

React Uploder

344 lines (275 loc) 9.34 kB
import React from 'react'; import PropTypes from 'prop-types'; import UploadHandler from 'resumablejs'; import UploaderComponent from "./UploaderComponent"; /** * Erzeugung der im State gespeicherten Datei. * * @param resumableFile ResumableFile-Objekt * @param removeFile Callback zum löschen der Datei */ const resumableFileToStateFile = (resumableFile, removeFile) => ({ uniqueIdentifier: resumableFile.uniqueIdentifier, name: resumableFile.fileName, size: resumableFile.size, progress: resumableFile.progress(), uploading: resumableFile.isUploading(), complete: resumableFile.isComplete(), error: null, responseContent: null, abort: () => resumableFile.abort(), cancel: () => removeFile(), retry: () => resumableFile.retry(), }); /** * Aktualisierung der im State gespeicherten Datei. * * @param stateFile * @param resumableFile * @param responseContent * @param error */ const updateStateFile = (stateFile, resumableFile, responseContent = null, error = null) => ({ ...stateFile, progress: resumableFile.progress(), uploading: resumableFile.isUploading(), complete: resumableFile.isComplete(), error: error, responseContent: responseContent, }); /** * Uploader zur Behandlung der Upload-Logik. */ class Uploader extends React.Component { constructor(props) { super(props); this.state = { uploaderState: this.props.autoStart ? 'complete' : 'paused', uploads: [], uploadsByIdentifier: {}, }; this.initializeUploadHandler(); } /** * Upload-Handler initialisieren und Events registrieren */ initializeUploadHandler() { this.uploadHandler = new UploadHandler({ target : this.props.path, simultaneousUploads: this.props.simultaneousUploads, chunkSize: this.props.chunkSize, testChunks: this.props.testChunks }); this.uploadHandler.on('fileAdded', upload => { const uniqueIdentifier = upload.uniqueIdentifier; const uploadsByIdentifier = { ...this.state.uploadsByIdentifier, [uniqueIdentifier]: resumableFileToStateFile( upload, () => this.removeFile(upload) ) }; const uploads = [ ...this.state.uploads, uniqueIdentifier ]; this.setState(() => ({ uploads, uploadsByIdentifier })); this.props.onFileAdded(uploadsByIdentifier[uniqueIdentifier], upload); if(this.props.autoStart) { this.startUpload(); } }); this.uploadHandler.on('fileProgress', upload => { const uniqueIdentifier = upload.uniqueIdentifier; const stateFile = this.state.uploadsByIdentifier[upload.uniqueIdentifier]; const uploadsByIdentifier = { ...this.state.uploadsByIdentifier, [uniqueIdentifier]: updateStateFile(stateFile, upload) }; this.setState({ uploadsByIdentifier }); this.props.onFileProgress(uploadsByIdentifier[uniqueIdentifier], upload); }); this.uploadHandler.on('fileSuccess', (upload, response) => { const uniqueIdentifier = upload.uniqueIdentifier; const stateFile = this.state.uploadsByIdentifier[uniqueIdentifier]; const responseContent = JSON.parse(response); const uploadsByIdentifier = { ...this.state.uploadsByIdentifier, [uniqueIdentifier]: updateStateFile(stateFile, upload, responseContent) }; this.setState({ uploadsByIdentifier }); this.props.onFileSuccess(uploadsByIdentifier[uniqueIdentifier], response, upload); if(this.props.clearFileOnSuccess) { this.removeFile(upload); } }); this.uploadHandler.on('fileError', (upload, error) => { const uniqueIdentifier = upload.uniqueIdentifier; const stateFile = this.state.uploadsByIdentifier[uniqueIdentifier]; const uploadsByIdentifier = { ...this.state.uploadsByIdentifier, [uniqueIdentifier]: updateStateFile(stateFile, upload, null, error) }; this.props.onFileError(uploadsByIdentifier[uniqueIdentifier], error, upload); this.setState({ uploadsByIdentifier }); }); this.uploadHandler.on('progress', () => { this.setState({ uploaderState: 'running', }); }); this.uploadHandler.on('pause', () => { this.setState({ uploaderState: 'paused', }); }); this.uploadHandler.on('complete', () => { this.setState({ uploaderState: this.props.autoStart ? 'complete' : 'paused', }); }); this.uploadHandler.on('cancel', () => { this.setState({ uploaderState: this.props.autoStart ? 'complete' : 'paused', uploads: [], uploadsByIdentifier: {}, }); }); } componentDidUpdate(prevProps, prevState) { const {uploaderState: prevUploaderState} = prevState; const {uploaderState: currentUploaderState} = this.state; if(prevUploaderState === currentUploaderState) { return; } const uploads = this.state.uploads.map(identifier => this.state.uploadsByIdentifier[identifier]); switch(currentUploaderState) { case 'complete': this.props.onUploaderComplete(uploads); break; case 'progress': this.props.onUploaderProgress(uploads); break; case 'pause': this.props.onUploaderPause(uploads); break; case 'cancel': this.props.onUploaderCancel(uploads); break; } } componentWillUnmount() { this.resetUpload(); // Events entfernen, damit diese nicht nach dem Unmounten der Komponente abgefeuert werden this.uploadHandler.events = []; } startUpload() { this.uploadHandler.upload(); } pauseUpload() { this.uploadHandler.pause(); } resetUpload() { this.uploadHandler.cancel(); } removeFile(upload) { this.uploadHandler.removeFile(upload); const uniqueIdentifier = upload.uniqueIdentifier; const uploads = this.state.uploads.filter(identifier => identifier !== uniqueIdentifier); let uploadsByIdentifier = {...this.state.uploadsByIdentifier}; delete uploadsByIdentifier[uniqueIdentifier]; this.setState({ uploads, uploadsByIdentifier }); } render() { const { component: Component, componentProps, } = this.props; const { uploaderState, uploads, uploadsByIdentifier } = this.state; return ( <Component uploaderState={uploaderState} uploads={uploads.map(identifier => uploadsByIdentifier[identifier])} addFile={file => this.uploadHandler.addFile(file)} assignDrop={domNode => this.uploadHandler.assignDrop(domNode)} assignBrowse={domNode => this.uploadHandler.assignBrowse(domNode)} start={() => this.startUpload()} pause={() => this.pauseUpload()} reset={() => this.resetUpload()} {...componentProps} /> ); } } Uploader.propTypes = { component: PropTypes.any.isRequired, componentProps: PropTypes.object, autoStart: PropTypes.bool, clearFileOnSuccess: PropTypes.bool, onUploaderComplete: PropTypes.func, onUploaderProgress: PropTypes.func, onUploaderPause: PropTypes.func, onUploaderCancel: PropTypes.func, onFileAdded: PropTypes.func, onFileSuccess: PropTypes.func, onFileProgress: PropTypes.func, onFileError: PropTypes.func, path: PropTypes.string.isRequired, simultaneousUploads: PropTypes.number, chunkSize: PropTypes.number, testChunks: PropTypes.bool }; Uploader.defaultProps = { component: UploaderComponent, componentProps: {}, autoStart: true, clearFileOnSuccess: false, onUploaderComplete: () => null, onUploaderProgress: () => null, onUploaderPause: () => null, onUploaderCancel: () => null, onFileAdded: () => null, onFileSuccess: () => null, onFileProgress: () => null, onFileError: () => null, simultaneousUploads: 1, chunkSize: 1024*1024, // 1MB testChunks: true }; export default Uploader;