Skip to content

Commit

Permalink
Added support to AWS S3 upload
Browse files Browse the repository at this point in the history
- deprecated `formDataParser` and replaced with `uploadDataHandler`
- deprecated `uploadHeader` and replaced with `uploadHeaderHandler`
- added corresponding deprecation messages and unit tests
  • Loading branch information
lionng429 committed Mar 28, 2018
1 parent 84bb9b9 commit 648007c
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 38 deletions.
48 changes: 32 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,16 @@ import { UploadManager } from 'react-file-uploader';
<UploadManager
component={STRING}
customClass={STRING_OR_ARRAY}
formDataParser={FUNCTION}
onUploadAbort={FUNCTION}
onUploadStart={FUNCTION}
onUploadProgress={FUNCTION}
onUploadEnd={FUNCTION}
progressDebounce={NUMBER}
reqConfigs={OBJECT}
style={OBJECT}
uploadDataHandler={FUNCTION}
uploadErrorHandler={FUNCTION}
uploadHeader={OBJECT}
uploadHeaderHandler={FUNCTION}
uploadUrl={STRING}
>
// UploadHandler as children
Expand All @@ -145,19 +145,7 @@ import { UploadManager } from 'react-file-uploader';

* component - `string`: the DOM tag name of the wrapper. By default it is an unordered list `ul`.
* customClass - `string | array`: the class name(s) for the wrapper
* formDataParser - `function`: the parser function of the form data to be send through the upload request.

```
/**
* @param formData {FormData} FormData instance
* @param fileData {File Object} File data object
* @returns {FormData} decorated FormData instance
*/
let formDataParser = (formData, fileData) => {
formData.append('file', fileData);
return formData;
}
```
* formDataParser - **DEPRECATED** this prop function is renamed as `uploadDataHandler` starting from v1.0.0.

* onUploadAbort - `function`: this will be fired when the upload request is aborted. This function is available from v1.0.0.

Expand Down Expand Up @@ -212,7 +200,21 @@ let onUploadEnd = (fileId, { error, progress, result, status }) => { ... }
* reqConfigs - `object`: the exposed superagent configs including `accept`, `method`, `timeout` and `withCredentials`.
* style - `object`: the style property for the wrapper.
* uploadUrl - `string` `required`: the url of the upload end point from your server.
* uploadHeader - `object`: the header object to be set as request header.
* uploadDataHandler - `function`: this function is to parse the data to be sent as request data. From v1.0.0, the first argument will become a upload task object instead of the File instance.

```
let uploadDataHandler = (upload) => {
// for FormData
const formData = new FormData();
formData.append('file', upload.data);
formData.append('custom-key', 'custom-value');
return formData;
// for AWS S3
return upload.data;
}
```

* uploadErrorHandler - `function`: this function is to process the arguments of `(err, res)` in `superagent.end()`. In this function, you can resolve the error and result according to your upload api response. Default implementation is available as defaultProps.

```
Expand All @@ -232,6 +234,20 @@ function uploadErrorHandler(err, res) {
}
```

* uploadHeader - **DEPRECATED** this prop is deprecated and replaced by `uploadHeaderHandler`.

* uploadHeaderHandler - `function`: the function is to parse the header object to be sent as request header.

```
let uploadHeaderHandler = (upload) => {
// for AWS S3
return {
'Content-Type': upload.data.type,
'Content-Disposition': 'inline'
};
}
```

# UploadHandler

Upload Handler helps you to execute the upload lifecycle, which is `start`, `progress` and `end`. It also acts as the presentation layer of a file, showing users the info of the **_uploading / uploaded_** file.
Expand Down
40 changes: 27 additions & 13 deletions src/UploadManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import classNames from 'classnames';
import bindKey from 'lodash/bindKey';
import clone from 'lodash/clone';
import debounce from 'lodash/debounce';
import isEmpty from 'lodash/isEmpty';
import superagent from 'superagent';
import uploadStatus from './constants/status';

Expand All @@ -21,6 +22,14 @@ class UploadManager extends Component {
}

componentDidMount() {
if (this.props.uploadHeader) {
console.warn('`props.uploadHeader` is DEPRECATED. Please use `props.uploadHeaderHandler` instead.');
}

if (this.props.formDataParser) {
console.warn('`props.formDataParser` is DEPRECATED. Please use `props.uploadDataHandler` instead.');
}

invariant(
!!this.props.uploadUrl,
'Upload end point must be provided to upload files'
Expand Down Expand Up @@ -72,21 +81,24 @@ class UploadManager extends Component {
},
onUploadStart,
onUploadEnd,
formDataParser,
uploadDataHandler,
uploadErrorHandler,
uploadHeader = {},
uploadHeaderHandler,
} = this.props;

if (typeof onUploadStart === 'function') {
onUploadStart(file.id, { status: uploadStatus.UPLOADING });
}

let formData = new FormData();
formData = formDataParser(formData, file.data);
let header = uploadHeaderHandler(file),
data = uploadDataHandler(file);

let request = superagent[method.toLowerCase()](url)
.accept(accept)
.set(uploadHeader);
.accept(accept);

if (!isEmpty(header)) {
request.set(header);
}

if (timeout) {
request.timeout(timeout);
Expand All @@ -101,7 +113,7 @@ class UploadManager extends Component {
debug(`start uploading file#${file.id} to ${url}`, file);

request
.send(formData)
.send(data)
.on('progress', ({ percent }) => this.onProgress(file.id, percent))
.end((err, res) => {
const { error, result } = uploadErrorHandler(err, res);
Expand Down Expand Up @@ -147,7 +159,6 @@ UploadManager.propTypes = {
PropTypes.string,
PropTypes.arrayOf(PropTypes.string),
]),
formDataParser: PropTypes.func,
onUploadAbort: PropTypes.func,
onUploadStart: PropTypes.func,
onUploadProgress: PropTypes.func,
Expand All @@ -163,19 +174,21 @@ UploadManager.propTypes = {
withCredentials: PropTypes.bool,
}),
style: PropTypes.object,
uploadDataHandler: PropTypes.func,
uploadErrorHandler: PropTypes.func,
uploadHeaderHandler: PropTypes.func,
uploadUrl: PropTypes.string.isRequired,
uploadHeader: PropTypes.object,
};

UploadManager.defaultProps = {
component: 'ul',
formDataParser: (formData, fileData) => {
formData.append('file', fileData);
return formData;
},
progressDebounce: 150,
reqConfigs: {},
uploadDataHandler: (file) => {
const formData = new FormData();
formData.append('file', file.data);
return formData;
},
uploadErrorHandler: (err, res = {}) => {
const body = res.body ? clone(res.body) : {};
let error = null;
Expand All @@ -190,6 +203,7 @@ UploadManager.defaultProps = {

return { error, result: body };
},
uploadHeaderHandler: (file) => ({})
};

export default UploadManager;
53 changes: 44 additions & 9 deletions src/__tests__/UploadManager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,32 @@ describe('UploadManager', () => {
onUploadStart,
onUploadProgress,
onUploadEnd,
formDataParser,
uploadDataHandler,
uploadHeaderHandler,
err,
errorResponse,
successResponse,
errorHandler,
file,
fileCopy;
fileCopy,
customHeader;

beforeEach(() => {
global.document = jsdom();
global.window = document.parentWindow;

customHeader = {
'Accept': 'customAccept',
'Content-Type': 'customContentType',
'Content-Disposition': 'customContentDisposition'
};

onUploadAbort = jest.genMockFn();
onUploadStart = jest.genMockFn();
onUploadProgress = jest.genMockFn();
onUploadEnd = jest.genMockFn();
formDataParser = jest.genMockFn();
uploadDataHandler = jest.genMockFn();
uploadHeaderHandler = jest.genMockFn().mockReturnValue(customHeader);

file = { id: 'fileId' };
fileCopy = JSON.parse(JSON.stringify(file));
Expand Down Expand Up @@ -75,7 +84,8 @@ describe('UploadManager', () => {
onUploadStart={onUploadStart}
onUploadProgress={onUploadProgress}
onUploadEnd={onUploadEnd}
formDataParser={formDataParser}
uploadDataHandler={uploadDataHandler}
uploadHeaderHandler={uploadHeaderHandler}
>
{children}
</UploadManager>
Expand Down Expand Up @@ -111,6 +121,15 @@ describe('UploadManager', () => {
});
});

describe('uploadDataHandler()', () => {
it('should return a FileData instance with a file data set', () => {
const file = { data: 'fileData' },
result = UploadManager.defaultProps.uploadDataHandler(file);
expect(result).toBeInstanceOf(FormData);
expect(result.get('file')).toEqual(file.data);
});
});

describe('uploadErrorHandler()', () => {
it('should return an object contains key of `error` and `result`', () => {
const result = errorHandler(null, successResponse);
Expand All @@ -132,13 +151,22 @@ describe('UploadManager', () => {
});
});

describe('uploadHeaderHandler()', () => {
it('should return an empty object', () => {
expect(UploadManager.defaultProps.uploadHeaderHandler()).toEqual({});
});
});

describe('upload()', () => {
it('should declare the request instance', () => {
const instance = uploadManager.instance();
instance.upload(instance.props.uploadUrl, file);

const request = instance.requests[file.id];
expect(request._timeout).toEqual(timeout);
expect(request._header.accept).toEqual(customHeader['Accept']);
expect(request._header['content-type']).toEqual(customHeader['Content-Type']);
expect(request._header['content-disposition']).toEqual(customHeader['Content-Disposition']);
});

it('should call `props.onUploadStart` function if it is given', () => {
Expand All @@ -148,11 +176,18 @@ describe('UploadManager', () => {
expect(file).toEqual(fileCopy);
});

it('should call `props.formDataParser` function if it is given', () => {
const instance = uploadManager.instance();
const data = {};
instance.upload(instance.props.uploadUrl, { data });
expect(formDataParser).toBeCalledWith(new FormData(), data);
it('should call `props.uploadDataHandler` function if it is given', () => {
const instance = uploadManager.instance(),
file = {};
instance.upload(instance.props.uploadUrl, file);
expect(uploadDataHandler).toBeCalledWith(file);
});

it('should call `props.uploadHeaderHandler` function if it is given', () => {
const instance = uploadManager.instance(),
file = {};
instance.upload(instance.props.uploadUrl, file);
expect(uploadHeaderHandler).toBeCalledWith(file);
});
});

Expand Down

0 comments on commit 648007c

Please sign in to comment.