├── .eslintignore ├── .npmignore ├── .prettierrc ├── .babelrc ├── LICENSE ├── .gitignore ├── webpack.config.js ├── .eslintrc ├── src ├── S3Uploader.css └── index.js ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | *.css 2 | build -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintignore 3 | .eslintrc 4 | .gitignore 5 | src -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": true, 4 | "singleQuote": true 5 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties" 8 | ] 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Oscar Barajas Tavares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node template 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | build/ 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # IDE 64 | .idea/* 65 | *.iml 66 | *.sublime-* 67 | 68 | # OSX 69 | .DS_Store 70 | .vscode 71 | 72 | # Docs Custom 73 | .cache/ 74 | yarn-error.log -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | path: path.resolve(__dirname, 'build'), 7 | filename: 'index.js', 8 | libraryTarget: 'commonjs2', 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.js$/, 14 | exclude: /(node_modules|build)/, 15 | use: { 16 | loader: 'babel-loader', 17 | }, 18 | }, 19 | { 20 | test: /\.css$/, 21 | exclude: /(node_modules|build)/, 22 | use: ['style-loader', 'css-loader'], 23 | }, 24 | ], 25 | }, 26 | externals: { 27 | react: { 28 | commonjs: 'react', 29 | commonjs2: 'react', 30 | amd: 'React', 31 | root: 'React', 32 | }, 33 | 'aws-sdk': { 34 | commonjs: 'aws-sdk', 35 | commonjs2: 'aws-sdk', 36 | amd: 'aws-sdk', 37 | root: 'aws-sdk', 38 | }, 39 | 'prop-types': { 40 | commonjs: 'prop-types', 41 | commonjs2: 'prop-types', 42 | amd: 'PropTypes', 43 | root: 'PropTypes', 44 | }, 45 | }, 46 | resolve: { 47 | extensions: ['.js', '.jsx', '.css'], 48 | alias: { 49 | react: path.resolve(__dirname, './node_modules/react'), 50 | 'prop-types': path.resolve(__dirname, './node_modules/prop-types'), 51 | }, 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "eslint:recommended", 5 | "airbnb" 6 | ], 7 | "plugins": [ 8 | "import", 9 | "react" 10 | ], 11 | "env": { 12 | "es6": true, 13 | "jest": true 14 | }, 15 | "rules": { 16 | "comma-dangle": [ 17 | "error", 18 | "always-multiline" 19 | ], 20 | "import/newline-after-import": [ 21 | "error", 22 | { 23 | "count": 1 24 | } 25 | ], 26 | "max-len": "off", 27 | "import/no-absolute-path": 0, 28 | "import/extensions": 0, 29 | "no-mixed-operators": 0, 30 | "import/no-unresolved": "off", 31 | "react/jsx-filename-extension": [ 32 | "error", 33 | { 34 | "extensions": [ 35 | ".js", 36 | ".jsx" 37 | ] 38 | } 39 | ], 40 | "react/destructuring-assignment": 0, 41 | "consistent-return": 0, 42 | "no-restricted-properties": [ 43 | "error", 44 | { 45 | "property": "lenght" 46 | } 47 | ], 48 | "import/no-extraneous-dependencies": [ 49 | "error", 50 | { 51 | "packageDir": "./" 52 | } 53 | ] 54 | }, 55 | "globals": { 56 | "window": true, 57 | "document": true, 58 | "localStorage": true, 59 | "FormData": true, 60 | "FileReader": true, 61 | "Blob": true, 62 | "navigator": true 63 | } 64 | } -------------------------------------------------------------------------------- /src/S3Uploader.css: -------------------------------------------------------------------------------- 1 | .S3Uploader-content { 2 | display: flex; 3 | } 4 | .S3Uploader-service { 5 | position: relative; 6 | display: inline-block; 7 | } 8 | .S3Uploader button { 9 | display: block; 10 | padding: 6px 12px; 11 | color: #000; 12 | border-radius: 0.4em; 13 | transition: background 0.3s; 14 | outline: none; 15 | } 16 | .S3Uploader input[type=file] { 17 | position: absolute; 18 | left: 0; 19 | top: 0; 20 | right: 0; 21 | bottom: 0; 22 | font-size: 1px; 23 | width: 100%; 24 | height: 100%; 25 | opacity: 0; 26 | cursor: pointer; 27 | } 28 | .S3Uploader-image { 29 | margin: 0 0 0 8px; 30 | } 31 | .S3Uploader-image img { 32 | width: 25px; 33 | height: 25px; 34 | object-fit: cover; 35 | border-radius: 4px; 36 | } 37 | .Loader { 38 | border: 4px solid #f3f3f3; 39 | border-top: 4px solid #5588EE; 40 | border-radius: 50%; 41 | width: 16px; 42 | height: 16px; 43 | animation: spin 4s linear infinite; 44 | } 45 | @-moz-keyframes spin { 46 | 0% { 47 | transform: rotate(0deg); 48 | } 49 | 100% { 50 | transform: rotate(360deg); 51 | } 52 | } 53 | @-webkit-keyframes spin { 54 | 0% { 55 | transform: rotate(0deg); 56 | } 57 | 100% { 58 | transform: rotate(360deg); 59 | } 60 | } 61 | @-o-keyframes spin { 62 | 0% { 63 | transform: rotate(0deg); 64 | } 65 | 100% { 66 | transform: rotate(360deg); 67 | } 68 | } 69 | @keyframes spin { 70 | 0% { 71 | transform: rotate(0deg); 72 | } 73 | 100% { 74 | transform: rotate(360deg); 75 | } 76 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-aws-s3-uploader", 3 | "version": "1.0.3", 4 | "description": "Simple React AWS S3 Uploader", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "format": "prettier --write '{*.js,src/**/*.{js,jsx}}'", 8 | "lint": "eslint src/ --fix", 9 | "start": "webpack --mode development --config webpack.config.js --watch", 10 | "build": "webpack --mode production --config webpack.config.js --progress" 11 | }, 12 | "prepublishOnly": "npm run build", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/gndx/react-aws-s3-uploader.git" 16 | }, 17 | "keywords": [ 18 | "react", 19 | "AWS", 20 | "S3", 21 | "file", 22 | "file-upload", 23 | "s3-upload", 24 | "react-s3" 25 | ], 26 | "author": { 27 | "name": "Oscar Barajas", 28 | "email": "oscar@arepa.dev" 29 | }, 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/gndx/react-aws-s3-uploader/issues" 33 | }, 34 | "homepage": "https://github.com/gndx/react-aws-s3-uploader#readme", 35 | "devDependencies": { 36 | "@babel/core": "^7.2.2", 37 | "@babel/plugin-proposal-class-properties": "^7.4.4", 38 | "@babel/preset-env": "^7.2.3", 39 | "@babel/preset-react": "^7.0.0", 40 | "babel-eslint": "^10.0.2", 41 | "babel-loader": "^8.0.5", 42 | "css-loader": "^3.0.0", 43 | "eslint": "^6.0.1", 44 | "eslint-config-airbnb": "^17.1.0", 45 | "eslint-config-prettier": "^6.0.0", 46 | "eslint-plugin-import": "^2.18.0", 47 | "eslint-plugin-jsx-a11y": "^6.2.2", 48 | "eslint-plugin-prettier": "^3.1.0", 49 | "eslint-plugin-react": "^7.14.2", 50 | "eslint-watch": "^5.1.2", 51 | "husky": "^2.7.0", 52 | "lint-staged": "^8.2.1", 53 | "prettier": "^1.18.2", 54 | "react": "^16.8.6", 55 | "style-loader": "^0.23.1", 56 | "webpack": "^4.35.0", 57 | "webpack-cli": "^3.3.5", 58 | "aws-sdk": "^2.485.0", 59 | "prop-types": "^15.7.2" 60 | }, 61 | "peerDependencies": { 62 | "aws-sdk": "^2.485.0", 63 | "react": "^16.8.6", 64 | "react-dom": "^16.2.0", 65 | "prop-types": "^15.7.2" 66 | }, 67 | "husky": { 68 | "hooks": { 69 | "pre-commit": "lint-staged" 70 | } 71 | }, 72 | "lint-staged": { 73 | "*.{js,jsx}": [ 74 | "npm run format", 75 | "npm run lint", 76 | "git add" 77 | ] 78 | } 79 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-aws-s3-uploader 2 | 3 | It provides an easy-to configure component to Uploading Photos to Amazon S3 from your React project. 4 | 5 | Based on this documentation: 6 | [Uploading Photos to Amazon S3 from a Browser"](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html) 7 | 8 | ![React](https://raw.githubusercontent.com/arepa-dev/reactAWSUploader/master/react-aws-s3-uploader.gif) 9 | 10 | 11 | # Install 12 | 13 | ```npm 14 | npm install react-aws-s3-uploader --save 15 | ``` 16 | 17 | Be sure to include the --save option to add this as a dependency in your application's package.json 18 | 19 | # Usage 20 | 21 | ```js 22 | import React, { Component } from 'react'; 23 | import S3Uploader from 'react-aws-s3-uploader'; // import the component 24 | 25 | class App extends Component { 26 | 27 | state = { 28 | file: '', 29 | } 30 | 31 | handleFile = (file) => { 32 | this.setState({ 33 | file 34 | }); 35 | } 36 | 37 | render() { 38 | return ( 39 | 44 | ); 45 | } 46 | } 47 | 48 | export default App; 49 | ``` 50 | 51 | # Options 52 | 53 | ### buttonName 54 | Name of button, default is "Upload File". 55 | 56 | ### bucketRegion 57 | The bucket region of AWS. 58 | 59 | ### albumBucketName 60 | Name of bucket where the files are stored. 61 | 62 | ### IdentityPoolId 63 | Amazon Cognito identity pools provide temporary AWS credentials for users who are guests (unauthenticated) and for users who have been authenticated and received a token. An identity pool is a store of user identity data specific to your account. More info: [Using Identity Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) 64 | 65 | ### handleFile 66 | funtion to return the url of the file stored in s3. 67 | 68 | ## Example: 69 | 70 | ```js 71 | 78 | ``` 79 | 80 | # Demo 81 | Check here: [react-aws-s3-uploader](https://arepa-dev.github.io/reactAWSUploader/) 82 | 83 | # Contributing 84 | If someone wants to add or improve something, I invite you to collaborate directly in this repository: [react-mailchimp-form](https://github.com/gndx/react-aws-s3-uploader) 85 | 86 | # License 87 | React-mailchimp-form is released under the [MIT License](https://opensource.org/licenses/MIT). 88 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import AWS from 'aws-sdk'; 4 | import './S3Uploader.css'; 5 | 6 | class S3Uploader extends Component { 7 | state = { 8 | loader: false, 9 | file: '', 10 | message: '', 11 | }; 12 | 13 | componentDidMount() { 14 | AWS.config.update({ 15 | region: this.props.bucketRegion, 16 | credentials: new AWS.CognitoIdentityCredentials({ 17 | IdentityPoolId: this.props.IdentityPoolId, 18 | }), 19 | }); 20 | } 21 | 22 | onChangeFile = () => { 23 | this.setState({ loader: true, file: '' }); 24 | const s3 = new AWS.S3({ 25 | apiVersion: '2006-03-01', 26 | params: { Bucket: this.props.albumBucketName }, 27 | }); 28 | const { files } = document.getElementById('photoupload'); 29 | if (!files.length) { 30 | return this.statusMessage('Please choose a file to upload first.'); 31 | } 32 | const file = files[0]; 33 | const fileName = file.name; 34 | s3.upload( 35 | { 36 | Key: fileName, 37 | Body: file, 38 | ACL: 'public-read', 39 | }, 40 | (err, data) => { 41 | if (err) { 42 | this.statusMessage(err); 43 | } else { 44 | this.setState({ loader: false, file: data.Location }); 45 | this.props.handleFile(data.Location); 46 | } 47 | } 48 | ); 49 | }; 50 | 51 | render() { 52 | const { file, message, loader } = this.state; 53 | const { buttonName } = this.props; 54 | return ( 55 |
56 |
57 |
58 | 59 | 65 |
66 |
67 | {loader ?
: null} 68 | {file ? fileUpload : null} 69 |
70 |
71 |
72 | {message > 0 ? message : null} 73 |
74 |
75 | ); 76 | } 77 | } 78 | 79 | S3Uploader.defaultProps = { 80 | buttonName: 'Upload File', 81 | bucketRegion: 'us-east-1', 82 | }; 83 | 84 | S3Uploader.propTypes = { 85 | buttonName: PropTypes.string, 86 | bucketRegion: PropTypes.string, 87 | albumBucketName: PropTypes.string.isRequired, 88 | IdentityPoolId: PropTypes.string.isRequired, 89 | handleFile: PropTypes.func.isRequired, 90 | }; 91 | 92 | export default S3Uploader; 93 | --------------------------------------------------------------------------------