├── .gitignore ├── LICENSE ├── README.md └── src ├── angularjs-nodejs ├── README.md ├── client.js ├── custom.css ├── index.html └── package.json └── s3-no-server ├── README.md ├── amazon-auth.js ├── aws-sdk-glue.js ├── custom.css ├── facebook-auth.js ├── fineuploader-glue.js ├── google-auth.js ├── index.html ├── loading.gif ├── not_available-generic.png ├── processing.gif └── waiting-generic.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.iml 3 | node_modules/ 4 | *custom.fineuploader* 5 | assets/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-present, Widen Enterprises, Inc. 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fine Uploader Integration Examples 2 | ====================== 3 | [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) 4 | 5 | Using [Fine Uploader][1] to solve real problems. 6 | 7 | 8 | Here are the examples covered in this repo: 9 | * [An AngularJS Directive using Fine Uploader UI and node.js][2] 10 | * [Uploading to S3 without a server][3] 11 | 12 | [1]: http://fineuploader.com 13 | [2]: src/angularjs-nodejs 14 | [3]: src/s3-no-server 15 | -------------------------------------------------------------------------------- /src/angularjs-nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Fine Uploader UI: AngularJS and node.js Integration 2 | 3 | 4 | Please read the [blog post that covers this example][1] in detail. 5 | 6 | [1]: https://blog.fineuploader.com/full-stack-javascript-image-uploader-using-angularjs-node-js-6e2fca020073 7 | -------------------------------------------------------------------------------- /src/angularjs-nodejs/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AngularJS directive for Fine Uploader UI jQuery (traditional endpoints). 3 | * Maintained by Widen Enterprises. 4 | * 5 | * This example: 6 | * - Delegates error messages to the dialog element. 7 | * - Generates client-side pre-upload image previews (where supported). 8 | * - Allows files to be excluded based on extension and MIME type (where supported). 9 | * - Determines the most appropriate upload button and drop zone text based on browser capabilities. 10 | * - Renders larger image preview on-demand in a dialog element. 11 | * - Keeps an aggregate progress bar up-to-date based on upload status for all files. 12 | * - Enables delete file support. 13 | * - Ensure newly submitted files are added to the top of the visible list. 14 | * - Enables chunking & auto-resume support. 15 | * 16 | * Requirements: 17 | * - Fine Uploader 5.4 or 5.5 18 | * - Dialog element polyfill 0.4.2 19 | * - AngularJS 1.5 20 | */ 21 | 22 | (function() { 23 | function isTouchDevice() { 24 | return "ontouchstart" in window || navigator.msMaxTouchPoints > 0; 25 | } 26 | 27 | function initButtonText($scope) { 28 | var input = document.createElement("input"); 29 | 30 | input.setAttribute("multiple", "true"); 31 | 32 | if (input.multiple === true && !qq.android()) { 33 | $scope.uploadButtonText = "Select Files"; 34 | } 35 | else { 36 | $scope.uploadButtonText = "Select a File"; 37 | } 38 | } 39 | 40 | function initDropZoneText($scope, $interpolate) { 41 | if (qq.supportedFeatures.folderDrop && !isTouchDevice()) { 42 | $scope.dropZoneText = "Drop Files or Folders Here"; 43 | } 44 | else if (qq.supportedFeatures.fileDrop && !isTouchDevice()) { 45 | $scope.dropZoneText = "Drop Files Here"; 46 | } 47 | else { 48 | $scope.dropZoneText = $scope.$eval($interpolate("Press '{{uploadButtonText}}'")); 49 | } 50 | } 51 | 52 | function bindToRenderedTemplate($compile, $scope, $interpolate, element) { 53 | $compile(element.contents())($scope); 54 | 55 | initButtonText($scope); 56 | initDropZoneText($scope, $interpolate); 57 | } 58 | 59 | function openLargerPreview($scope, uploader, modal, size, fileId) { 60 | uploader.drawThumbnail(fileId, new Image(), size).then(function(image) { 61 | $scope.largePreviewUri = image.src; 62 | $scope.$apply(); 63 | modal.showModal(); 64 | }); 65 | } 66 | 67 | function closePreview(modal) { 68 | modal.close(); 69 | } 70 | 71 | angular.module("fineUploaderDirective", []) 72 | .directive("fineUploader", function($compile, $interpolate) { 73 | return { 74 | restrict: "A", 75 | replace: true, 76 | 77 | link: function($scope, element, attrs) { 78 | var endpoint = attrs.uploadServer, 79 | notAvailablePlaceholderPath = attrs.notAvailablePlaceholder, 80 | waitingPlaceholderPath = attrs.waitingPlaceholder, 81 | acceptFiles = attrs.allowedMimes, 82 | sizeLimit = attrs.maxFileSize, 83 | largePreviewSize = parseInt(attrs.largePreviewSize), 84 | allowedExtensions = JSON.parse(attrs.allowedExtensions), 85 | previewDialog = document.querySelector('.large-preview'), 86 | 87 | uploader = new qq.FineUploader({ 88 | debug: true, 89 | element: element[0], 90 | request: {endpoint: endpoint}, 91 | 92 | validation: { 93 | acceptFiles: acceptFiles, 94 | allowedExtensions: allowedExtensions, 95 | sizeLimit: sizeLimit 96 | }, 97 | 98 | deleteFile: { 99 | endpoint: endpoint, 100 | enabled: true 101 | }, 102 | 103 | thumbnails: { 104 | placeholders: { 105 | notAvailablePath: notAvailablePlaceholderPath, 106 | waitingPath: waitingPlaceholderPath 107 | } 108 | }, 109 | 110 | display: { 111 | prependFiles: true 112 | }, 113 | 114 | failedUploadTextDisplay: { 115 | mode: "custom" 116 | }, 117 | 118 | retry: { 119 | enableAuto: true 120 | }, 121 | 122 | chunking: { 123 | enabled: true 124 | }, 125 | 126 | resume: { 127 | enabled: true 128 | }, 129 | 130 | callbacks: { 131 | onSubmitted: function(id, name) { 132 | var fileEl = this.getItemByFileId(id), 133 | thumbnailEl = fileEl.querySelector('.thumbnail-button'); 134 | 135 | thumbnailEl.addEventListener('click', function() { 136 | openLargerPreview($scope, uploader, previewDialog, largePreviewSize, id); 137 | }); 138 | } 139 | } 140 | }); 141 | 142 | dialogPolyfill.registerDialog(previewDialog); 143 | $scope.closePreview = closePreview.bind(this, previewDialog); 144 | bindToRenderedTemplate($compile, $scope, $interpolate, element); 145 | } 146 | } 147 | }); 148 | })(); 149 | -------------------------------------------------------------------------------- /src/angularjs-nodejs/custom.css: -------------------------------------------------------------------------------- 1 | .thumbnail-button { 2 | border: none; 3 | background-color: transparent; 4 | padding: 0; 5 | } 6 | 7 | .large-preview { 8 | width: inherit; 9 | right: inherit; 10 | height: inherit; 11 | margin: inherit; 12 | position: fixed; 13 | top: 50%; 14 | left: 50%; 15 | transform: translate(-50%, -50%); 16 | } 17 | 18 | .large-preview button { 19 | position: absolute; 20 | top: 0; 21 | right: 0; 22 | } -------------------------------------------------------------------------------- /src/angularjs-nodejs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | 25 | 26 | 27 | Fine Uploader AngularJS Integration Example 28 | 29 | 30 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/angularjs-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "fineUploaderAngularExample", 3 | "description" : "Fine Uploader Angular & NodeJS example", 4 | "dependencies" : { 5 | "angular": "1.4.x", 6 | "dialog-polyfill": "0.4.2", 7 | "fine-uploader": "~5.x.x", 8 | "fine-uploader-traditional-server": "3.0.2" 9 | }, 10 | "engines": { 11 | "node" : ">=5.0.0" 12 | }, 13 | "version" : "1.0.0", 14 | "scripts": { 15 | "server": "PUBLIC_DIR=$(pwd) NODE_MODULES_DIR=$(pwd)/node_modules/ UPLOADED_FILES_DIR=$(pwd)/uploads/ node node_modules/fine-uploader-traditional-server/nodejs" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/s3-no-server/README.md: -------------------------------------------------------------------------------- 1 | # Fine Uploader S3: Uploading file to S3 without writing any server-side code 2 | 3 | ### All you need is a simple HTTP server to host the HTML/javascript files. 4 | 5 | There is a live demo using this code at https://fineuploader-s3-client-demo.s3.amazonaws.com/index.html. 6 | 7 | Please read the [blog post that covers this example][1] in detail. 8 | 9 | You can also read more on Fine Uploader's client-side signing feature (new as of 4.2) on the [associated feature's 10 | documentation page][2]. 11 | 12 | [1]: https://blog.fineuploader.com/uploads-without-any-server-code-3d7219167591 13 | [2]: http://docs.fineuploader.com/features/no-server-uploads.html 14 | -------------------------------------------------------------------------------- /src/s3-no-server/amazon-auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles the Amazon login workflow. 3 | */ 4 | $(function() { 5 | // Called when the SDK is loaded & ready 6 | var onAmazonLoginReady = function() { 7 | amazon.Login.setClientId("amzn1.application-oa2-client.86174d2cae1d4fe9950b7f31f4d1e3aa"); 8 | }, 9 | 10 | sdkScriptEl = document.createElement('script'); 11 | 12 | // Prepare the script tag used to load the SDK 13 | sdkScriptEl.type = "text/javascript"; 14 | sdkScriptEl.async = true; 15 | sdkScriptEl.id = "amazon-login-sdk"; 16 | sdkScriptEl.src = "https://api-cdn.amazon.com/sdk/login1.js"; 17 | document.getElementById("amazon-root").appendChild(sdkScriptEl); 18 | 19 | // Called when the login button is clicked 20 | document.getElementById("amazon-signin").onclick = function() { 21 | // Only allow access to profile, and always require re-auth 22 | var options = { 23 | scope : "profile", 24 | interactive: "always" 25 | }; 26 | 27 | // Attempt to authorize the user (results in pop-up) 28 | amazon.Login.authorize(options) 29 | // Called when the auth attempt has completed 30 | .onComplete(function(authResult) { 31 | // If authorized... 32 | if (authResult.status === "complete") { 33 | var expiresInMs = parseInt(authResult.expires_in) * 1000; 34 | 35 | $(document).trigger("tokenReceived.s3Demo"); 36 | 37 | // Get the authenticated user's name (for file storage) 38 | amazon.Login.retrieveProfile(function(response) { 39 | s3DemoGlobals.userName = response.profile.Name; 40 | }); 41 | 42 | // Grab S3 credentials using the bearer token 43 | s3DemoGlobals.assumeRoleWithWebIdentity({ 44 | idToken: authResult.access_token, 45 | roleArn: "arn:aws:iam::776099607611:role/demo-s3-noserver-amazon", 46 | providerId: "www.amazon.com" 47 | }); 48 | 49 | // Make the user re-auth just before the token expires. 50 | setTimeout(function() { 51 | alert("Token expired. You must sign in again."); 52 | $(document).trigger("tokenExpired.s3Demo"); 53 | }, expiresInMs - 10000) 54 | } 55 | }); 56 | return false; 57 | }; 58 | 59 | window.onAmazonLoginReady = onAmazonLoginReady; 60 | 61 | // Show the login button if there is no usable token 62 | $(document).on("tokenExpired.s3Demo", function() { 63 | $("#amazon-signin").show(); 64 | }); 65 | 66 | // Hide the login button if a usable token exists 67 | $(document).on("tokenReceived.s3Demo", function() { 68 | $("#amazon-signin").hide(); 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /src/s3-no-server/aws-sdk-glue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grabs AWS credentials for an authenticated user. 3 | */ 4 | $(function() { 5 | var assumeRoleWithWebIdentity = function(params) { 6 | var sts = new AWS.STS(), 7 | assumeRoleParams = {}; 8 | 9 | s3DemoGlobals.roleArn = params.roleArn || s3DemoGlobals.roleArn; 10 | s3DemoGlobals.providerId = params.providerId || s3DemoGlobals.providerId; 11 | s3DemoGlobals.idToken = params.idToken || s3DemoGlobals.idToken; 12 | 13 | assumeRoleParams = { 14 | RoleArn: s3DemoGlobals.roleArn, 15 | RoleSessionName: "web-identity-federation", 16 | WebIdentityToken: s3DemoGlobals.idToken 17 | }; 18 | 19 | if (s3DemoGlobals.providerId) { 20 | assumeRoleParams.ProviderId = s3DemoGlobals.providerId; 21 | } 22 | 23 | sts.assumeRoleWithWebIdentity(assumeRoleParams, params.callback || s3DemoGlobals.updateCredentials); 24 | }, 25 | getFuCredentials = function(data) { 26 | return { 27 | accessKey: data.Credentials.AccessKeyId, 28 | secretKey: data.Credentials.SecretAccessKey, 29 | sessionToken: data.Credentials.SessionToken, 30 | expiration: data.Credentials.Expiration 31 | }; 32 | }; 33 | 34 | s3DemoGlobals.assumeRoleWithWebIdentity = assumeRoleWithWebIdentity; 35 | s3DemoGlobals.getFuCredentials = getFuCredentials; 36 | }()); 37 | -------------------------------------------------------------------------------- /src/s3-no-server/custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | CSS for Fine Uploader UI. Depends on a matching HTML file. 4 | Maintained by Widen Enterprises. 5 | 6 | See the HTML file comments for more details. 7 | 8 | */ 9 | 10 | .demo-title 11 | { 12 | text-align: center; 13 | font-weight: 200; 14 | margin-top: 0px; 15 | margin-bottom: 40px; 16 | color: #00abc7; 17 | font-size: 36px; 18 | } 19 | 20 | .uploader-drop-zone 21 | { 22 | /* Static height drop zone that also contains the file list, with a border */ 23 | height: 300px; 24 | border: solid 1px black; 25 | -webkit-border-radius: 8px; 26 | -moz-border-radius: 8px; 27 | border-radius: 8px; 28 | text-align: center; 29 | 30 | /* Make the drop zone scollable */ 31 | overflow-y: scroll; 32 | } 33 | 34 | LI.file-container 35 | { 36 | /* Make sure the drop zone text is always at least a little visible */ 37 | opacity: .6; 38 | 39 | /* Rounded file container corners */ 40 | -webkit-border-radius: 8px; 41 | -moz-border-radius: 8px; 42 | border-radius: 8px; 43 | 44 | margin: 10px; 45 | 46 | /* Used to un-center the text for each file item, 47 | otherwise the .uploader-drop-zone text-align style will apply */ 48 | text-align: left; 49 | } 50 | 51 | .qq-uploader 52 | { 53 | margin: 20px 0; 54 | width: auto; 55 | } 56 | 57 | .qq-upload-button 58 | { 59 | margin-bottom: 10px; 60 | 61 | /* Rounded upload button corners */ 62 | -webkit-border-radius: 4px; 63 | -moz-border-radius: 4px; 64 | border-radius: 4px; 65 | } 66 | 67 | .drop-zone-text 68 | { 69 | /* Vertically & horizontally center the dynamically generated drop zone message 70 | inside the drop zone & make it a bit presentable */ 71 | position: absolute; 72 | vertical-align: middle; 73 | line-height: 300px; 74 | font-size: 200%; 75 | width: 100%; 76 | left: 0; 77 | color: #cccccc; 78 | } 79 | 80 | .file-info 81 | { 82 | display: inline-block; 83 | width: 400px; 84 | } 85 | 86 | IMG 87 | { 88 | vertical-align: middle; 89 | } 90 | 91 | .signin-button 92 | { 93 | margin-bottom: 20px; 94 | display:block; 95 | } 96 | 97 | body 98 | { 99 | background-color: #FEFEFC; 100 | font-family: 'Maven Pro', sans-serif; 101 | padding: 40px 0; 102 | } 103 | 104 | a 105 | { 106 | color: #0088CC; 107 | text-decoration: none; 108 | font-size: 14px; 109 | } 110 | 111 | a:hover 112 | { 113 | text-decoration: underline; 114 | } 115 | 116 | p 117 | { 118 | font-size: 14px; 119 | } 120 | 121 | h2 122 | { 123 | font-weight: 200; 124 | margin-bottom: 5px; 125 | color: #00abc7; 126 | font-size: 36px; 127 | } 128 | 129 | h3 130 | { 131 | font-weight:300; 132 | font-size: 24px; 133 | line-height: 28px; 134 | color: #00abc7; 135 | } 136 | 137 | h4 138 | { 139 | color: #e65c47; 140 | font-weight: 300; 141 | } 142 | 143 | .wrapper 144 | { 145 | width: 80%; 146 | max-width: 1024px; 147 | margin: 0 auto; 148 | } 149 | 150 | .sign-in-buttons 151 | { 152 | text-align: center; 153 | } 154 | 155 | .hero-unit 156 | { 157 | margin-top:30px; 158 | padding: 20px 40px; 159 | line-height: 18px; 160 | } 161 | 162 | 163 | 164 | .footer 165 | { 166 | padding: 70px 0; 167 | border-top: 1px solid #e5e5e5; 168 | background-color: #525252; 169 | color :#eee; 170 | position: relative; 171 | left: 0; 172 | bottom: 0; 173 | height: 120px; 174 | width: 100%; 175 | margin-top: 60px; 176 | } 177 | .footer p 178 | { 179 | margin-bottom: 0; 180 | color: #eee; 181 | } 182 | .footer-links 183 | { 184 | margin: 10px 0; 185 | } 186 | .footer-links li 187 | { 188 | display: inline; 189 | margin-right: 10px; 190 | } 191 | .footer a 192 | { 193 | color: #66ccdd; 194 | } 195 | .widenIcon 196 | { 197 | margin: -3px 0px 0px 4px; 198 | } -------------------------------------------------------------------------------- /src/s3-no-server/facebook-auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles the Facebook login workflow. 3 | */ 4 | $(function() { 5 | var scriptId = "facebook-jssdk", 6 | refScript = document.getElementsByTagName("script")[0], 7 | autoLogout = true, 8 | sdkScript; 9 | 10 | // Sets up the script tag used to load the SDK 11 | if (!document.getElementById(scriptId)) { 12 | sdkScript = document.createElement("script"); 13 | sdkScript.id = scriptId; 14 | sdkScript.async = true; 15 | sdkScript.src = "//connect.facebook.net/en_US/all.js"; 16 | 17 | refScript.parentNode.insertBefore(sdkScript, refScript); 18 | } 19 | 20 | // Called when the SDK is loaded and ready 21 | window.fbAsyncInit = function() { 22 | // Initialize the login workflow 23 | FB.init({ 24 | appId : "494966370620626", 25 | status : true, 26 | cookie : false, 27 | xfbml : false 28 | }); 29 | 30 | // Called when the login button is clicked 31 | $("#facebook-signin").click(function() { 32 | // Ensure we don't de-auth the user if they click on the login button 33 | autoLogout = false; 34 | FB.login(); 35 | }); 36 | 37 | // Called when auth attempt has completed 38 | FB.Event.subscribe('auth.authResponseChange', function(response) { 39 | // Successfully authenticated 40 | if (response.status === 'connected') { 41 | // Force FB to delete auth credentials for the user on page load. 42 | // This ensure the user has to re-authenticate on page load. Probably only useful for this demo. 43 | if (autoLogout) { 44 | FB.api("/me/permissions", "DELETE"); 45 | } 46 | else { 47 | (function() { 48 | var expiresInMs = parseInt(response.authResponse.expiresIn) * 1000; 49 | $(document).trigger("tokenReceived.s3Demo"); 50 | 51 | // Grab the authenticated user's name via the Graph API (for file storage) 52 | FB.api("/me", function(response) { 53 | s3DemoGlobals.userName = response.name; 54 | }); 55 | 56 | // Grab S3 credentials for the user 57 | s3DemoGlobals.assumeRoleWithWebIdentity({ 58 | idToken: response.authResponse.accessToken, 59 | roleArn: "arn:aws:iam::776099607611:role/demo-s3-noserver-facebook", 60 | providerId: "graph.facebook.com" 61 | }); 62 | 63 | // Ensure the user is asked to re-auth just before the token expires 64 | setTimeout(function() { 65 | alert("Token expired. You must sign in again."); 66 | $(document).trigger("tokenExpired.s3Demo"); 67 | }, expiresInMs - 10000) 68 | }()); 69 | } 70 | } 71 | else { 72 | $(document).trigger("tokenExpired.s3Demo"); 73 | } 74 | }); 75 | }; 76 | 77 | // Show the login button if we don't have a valid token to use 78 | $(document).on("tokenExpired.s3Demo", function() { 79 | $("#facebook-signin").show(); 80 | }); 81 | 82 | // Hide the login button if we do have a valid token to use 83 | $(document).on("tokenReceived.s3Demo", function() { 84 | $("#facebook-signin").hide(); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /src/s3-no-server/fineuploader-glue.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sets up a Fine Uploader S3 jQuery UI instance, ensures files are saved under a "directory" in the bucket 3 | * bearing the logged-in user's name, provides a link to view the uploaded file after it has reached the bucket 4 | * and asks AWS for new credentials before those expire. 5 | */ 6 | $(function() { 7 | var bucketUrl = "https://fineuploader-s3-client-demo-uploads.s3.amazonaws.com", 8 | updateCredentials = function(error, data) { 9 | if (!error) { 10 | $('#uploader').fineUploaderS3("setCredentials", s3DemoGlobals.getFuCredentials(data)); 11 | } 12 | }, 13 | hideUploader = function() { 14 | $("#uploader").hide(); 15 | }; 16 | 17 | $("#uploader").fineUploaderS3({ 18 | request: { 19 | endpoint: bucketUrl 20 | }, 21 | objectProperties: { 22 | // Since we want all items to be publicly accessible w/out a server to return a signed URL 23 | acl: "public-read", 24 | 25 | // The key for each file will follow this format: {USER_NAME}/{UUID}.{FILE_EXTENSION} 26 | key: function(id) { 27 | var filename = this.getName(id), 28 | uuid = this.getUuid(id); 29 | 30 | return qq.format("{}/{}.{}", s3DemoGlobals.userName, uuid, qq.getExtension(filename)); 31 | } 32 | }, 33 | chunking: { 34 | enabled: true 35 | }, 36 | resume: { 37 | enabled: true 38 | }, 39 | // Restrict files to 15 MB and 5 net files per session 40 | validation: { 41 | itemLimit: 5, 42 | sizeLimit: 15000000 43 | }, 44 | thumbnails: { 45 | placeholders: { 46 | notAvailablePath: "not_available-generic.png", 47 | waitingPath: "waiting-generic.png" 48 | } 49 | } 50 | }) 51 | .on('complete', function(event, id, name, response, xhr) { 52 | var $fileEl = $(this).fineUploaderS3("getItemByFileId", id), 53 | $viewBtn = $fileEl.find(".view-btn"), 54 | key = $(this).fineUploaderS3("getKey", id); 55 | 56 | // Add a "view" button to access the uploaded file in S3 if the upload is successful 57 | if (response.success) { 58 | $viewBtn.show(); 59 | $viewBtn.attr("href", bucketUrl + "/" + key); 60 | } 61 | }) 62 | .on("credentialsExpired", function() { 63 | var promise = new qq.Promise(); 64 | 65 | // Grab new credentials 66 | s3DemoGlobals.assumeRoleWithWebIdentity({ 67 | callback: function(error, data) { 68 | if (error) { 69 | promise.failure("Failed to assume role"); 70 | } 71 | else { 72 | promise.success(s3DemoGlobals.getFuCredentials(data)); 73 | } 74 | } 75 | }); 76 | 77 | return promise; 78 | }); 79 | 80 | s3DemoGlobals.updateCredentials = updateCredentials; 81 | 82 | $(document).on("tokenExpired.s3Demo", hideUploader); 83 | $(document).on("tokenReceived.s3Demo", function() { 84 | $("#uploader").show(); 85 | }); 86 | $(document).trigger("tokenExpired.s3Demo"); 87 | }); 88 | -------------------------------------------------------------------------------- /src/s3-no-server/google-auth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles the Google login workflow. 3 | */ 4 | $(function() { 5 | var plusOnScriptEl = document.createElement('script'), 6 | referenceScriptEl = document.getElementsByTagName('script')[0], 7 | 8 | // Called when the auth attempt has completed 9 | s3GoogleOauthHandler = function(authResult) { 10 | // If authenticated... 11 | if (authResult.status.signed_in) { 12 | var expiresInMs = parseInt(authResult.expires_in) * 1000; 13 | 14 | $(document).trigger("tokenReceived.s3Demo"); 15 | 16 | setUserName(authResult.access_token); 17 | 18 | // Get S3 credentials 19 | s3DemoGlobals.assumeRoleWithWebIdentity({ 20 | roleArn: "arn:aws:iam::776099607611:role/demo-s3-noserver-google", 21 | idToken: authResult.id_token 22 | }); 23 | 24 | // Ensure the user is asked to re-auth before the token expires 25 | setTimeout(function() { 26 | alert("Token expired. You must sign in again."); 27 | $(document).trigger("tokenExpired.s3Demo"); 28 | }, expiresInMs - 10000) 29 | } 30 | else { 31 | $(document).trigger("tokenExpired.s3Demo"); 32 | } 33 | }, 34 | 35 | showButton = function() { 36 | $("#google-signin").show(); 37 | }, 38 | 39 | // Grabs the authenticated user's name (for file storage) 40 | setUserName = function(accessToken) { 41 | var xhr = new XMLHttpRequest(); 42 | 43 | xhr.onload = function() { 44 | if (xhr.status === 200) { 45 | var userName = JSON.parse(xhr.responseText).displayName; 46 | 47 | s3DemoGlobals.userName = userName; 48 | } 49 | }; 50 | 51 | xhr.open("GET", "https://www.googleapis.com/plus/v1/people/me?access_token=" + accessToken); 52 | xhr.send(); 53 | }; 54 | 55 | window.s3GoogleOauthHandler = s3GoogleOauthHandler; 56 | 57 | // Setup the script tag used to load the SDK 58 | plusOnScriptEl.type = "text/javascript"; 59 | plusOnScriptEl.async = true; 60 | plusOnScriptEl.src = "https://plus.google.com/js/client:plusone.js"; 61 | referenceScriptEl.parentNode.insertBefore(plusOnScriptEl, referenceScriptEl); 62 | 63 | $(document).on("tokenExpired.s3Demo", showButton); 64 | $(document).on("tokenReceived.s3Demo", function() { 65 | $("#google-signin").hide(); 66 | }); 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /src/s3-no-server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 | S3 server-less example 31 | 32 | 33 | 34 | 35 | 36 | 37 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
83 | 84 |
85 | 86 |

Upload files to Amazon S3 - No server-side code required

87 | 88 | 89 | 90 | 91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 |
106 |

Don't want to write ANY server-side code at all?

107 |

Leave EVERYTHING up to the browser and S3.

108 |
109 |

Fine Uploader can handle the S3 request signing for you. Just provide temporary credentials for 110 | your locked-down IAM user and pass them off to Fine Uploader S3. You can do this all client-side, 111 | easily and securely, using Google, Facebook, or Amazon as an identity provider, and the AWS JavaScript SDK. 112 | Need more details? Read the blog post that covers step-by-step setup for this workflow.

113 | 114 |

This demo uses only client-side code to authenticate and upload files to S3. We have not written 115 | any server-side code at all. In fact, the entire example is hosted in an S3 bucket. Try it out 116 | by choosing one of the identity providers to the right.

117 | 118 |
119 |
120 | 121 |
122 | 150 |
151 |
152 | 153 |
154 | 155 |
156 | 157 |

Additional links:

158 | 159 |

160 |

165 |

166 | 167 |

Note: If you simply want to avoid the signature HTTP requests for each file/chunk, and need 168 | to support legacy browsers, you can do that too, but some server-side code will be required 169 | to utilize the AWS temporary credentials calls. See the client-side signing feature page 170 | in the documentation for more details.

171 | 172 | 173 | 174 | 177 |
178 | 179 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /src/s3-no-server/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FineUploader/integration-examples/95f03e8abd7a35fcd4a5fd42a58f0b584eed38fc/src/s3-no-server/loading.gif -------------------------------------------------------------------------------- /src/s3-no-server/not_available-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FineUploader/integration-examples/95f03e8abd7a35fcd4a5fd42a58f0b584eed38fc/src/s3-no-server/not_available-generic.png -------------------------------------------------------------------------------- /src/s3-no-server/processing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FineUploader/integration-examples/95f03e8abd7a35fcd4a5fd42a58f0b584eed38fc/src/s3-no-server/processing.gif -------------------------------------------------------------------------------- /src/s3-no-server/waiting-generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FineUploader/integration-examples/95f03e8abd7a35fcd4a5fd42a58f0b584eed38fc/src/s3-no-server/waiting-generic.png --------------------------------------------------------------------------------