@boewa-software/react-async-uploader
Version:
React Uploder
344 lines (275 loc) • 9.34 kB
JSX
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;