Last active
January 9, 2018 17:54
-
-
Save jsmithdev/f4be09961a0f9c1d62c79409e0026e76 to your computer and use it in GitHub Desktop.
Example of reducing an image size client side before uploading -- in this case to SalesForce
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- Example of controller | |
| public class QuickPix { | |
| public String parentId {get;set;} | |
| public String sessionId {get;set;} | |
| public QuickPix(){ | |
| parentId = ApexPages.currentPage().getParameters().get('id');//Use param as attachments parent uid; | |
| sessionId = UserInfo.getSessionId(); | |
| } | |
| } | |
| --> | |
| <apex:page controller="QuickPix" standardstylesheets="false"> | |
| <apex:SLDS /> | |
| <style> | |
| .background { | |
| background-color: #eee; | |
| padding: .1rem 0 1rem 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .container { | |
| margin: 0 auto; | |
| } | |
| .card { | |
| background: #FFF !important; | |
| padding: 1rem; | |
| margin: 2rem 10% 2rem 10%; | |
| box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); | |
| } | |
| .content { | |
| margin: 0 auto; | |
| } | |
| .hide { | |
| display: none; | |
| } | |
| </style> | |
| <div class="background"> | |
| <div class="container"> | |
| <div class="card"> | |
| <div class="content"> | |
| <h3>Capture Example</h3> | |
| <div class="slds-form-element"> | |
| <div class="slds-form-element__control"> | |
| <div class="slds-file-selector slds-file-selector_files"> | |
| <div class="slds-file-selector__dropzone"> | |
| <input accept="image/*" capture="true" class="capture slds-file-selector__input slds-assistive-text" type="file" aria-labelledby="file-selector-primary-label file-selector-secondary-label" /> | |
| <label class="slds-file-selector__body" for="capture" id="file-selector-secondary-label"> | |
| <span class="sudoCapture slds-file-selector__button slds-button slds-button_neutral"> | |
| <svg class="slds-button__icon slds-button__icon_left" aria-hidden="true"> | |
| <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="{!URLFOR($Asset.SLDS, '/assets/icons/utility-sprite/svg/symbols.svg#upload')}" /> | |
| </svg>Take & Upload Photo | |
| </span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <br /> | |
| <br /> | |
| <h3>Select Example</h3> | |
| <div class="slds-form-element"> | |
| <div class="slds-form-element__control"> | |
| <div class="slds-file-selector slds-file-selector_files"> | |
| <div class="slds-file-selector__dropzone"> | |
| <input accept="image/*" class="select slds-file-selector__input slds-assistive-text" type="file" aria-labelledby="file-selector-primary-label file-selector-secondary-label" /> | |
| <label class="slds-file-selector__body" for="capture" id="file-selector-secondary-label"> | |
| <span class="sudoSelect slds-file-selector__button slds-button slds-button_neutral"> | |
| <svg class="slds-button__icon slds-button__icon_left" aria-hidden="true"> | |
| <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="{!URLFOR($Asset.SLDS, '/assets/icons/utility-sprite/svg/symbols.svg#upload')}" /> | |
| </svg>Upload Local Photo | |
| </span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <br /> | |
| <br /> | |
| <h3>Thumbs</h3> | |
| <div class="thumbs"></div> | |
| <br /> | |
| <br /> | |
| <i>Dev Note: only thing different on inputs is capture attribute. Capture has devices jump strait to the camera to take a picture | |
| whereas the other has user select camera or file from device's local storage. Takes a step out if all they want is quickly snap pics. | |
| <br/> | |
| This Uploads onchange of input -- so immediate. Remoting may take less time, perhaps only milliseconds, but has a file cap equvilant of less than 5MB. | |
| Today's cameras could blow that out no prob. so this uses Org's own SOAP/AJAX API. Hopfully that works as it's not open to the public therefore not going to hit limits (hopefully) but may want to run those numbers to be sure. | |
| </i> | |
| <canvas id="myCanvas" class="hide" /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| const __sfdcSessionId = '{!sessionId}' | |
| </script> | |
| <script src="/soap/ajax/34.0/connection.js" type="text/javascript"></script> | |
| <script> | |
| /* SETUP EL's */ | |
| this.dom = {} | |
| this.dom.select = document.querySelector('.select') | |
| this.dom.sudoSelect = document.querySelector('.sudoSelect') | |
| this.dom.capture = document.querySelector('.capture') | |
| this.dom.sudoCapture = document.querySelector('.sudoCapture') | |
| this.dom.thumbs = document.querySelector('.thumbs') | |
| /* Add Listeners to ELs */ | |
| this.dom.sudoSelect.onclick = () => this.dom.select.click() | |
| this.dom.sudoCapture.onclick = () => this.dom.capture.click() | |
| this.dom.select.onchange = e => constrict(e.target.files[0]) | |
| this.dom.capture.onchange = e => constrict(e.target.files[0]) | |
| /* Helpers */ | |
| const appendThumb = (el) => this.dom.thumbs.appendChild(el) | |
| const constrict = (file) => { | |
| const MAX_HEIGHT = 200; | |
| //console.log('START') | |
| //console.dir(file) | |
| const image = new Image(); | |
| image.onload = function() { | |
| const canvas = document.getElementById("myCanvas"); | |
| if (image.height > MAX_HEIGHT) { | |
| image.width *= MAX_HEIGHT / image.height; | |
| image.height = MAX_HEIGHT; | |
| } | |
| const ctx = canvas.getContext("2d"); | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| canvas.width = image.width; | |
| canvas.height = image.height; | |
| ctx.drawImage(image, 0, 0, image.width, image.height); | |
| //append lower quality image | |
| appendThumb(image) | |
| canvas.toBlob((blob) => | |
| addAttachment(new File([blob], file.name, { | |
| type: 'image/jpeg' | |
| })), 'image/jpeg', 0.55) | |
| } | |
| image.src = URL.createObjectURL(file); | |
| } | |
| const addAttachment = (file) => { | |
| //Final File, attaching | |
| //console.dir(file) | |
| const reader = new FileReader() | |
| if (file == undefined) { | |
| alert('Attachment required to proceed'); | |
| return false | |
| } | |
| if (file.size > 26214400) { //Where 26214400 is byte equivalent of 25MB | |
| alert('Attachment size not supported'); | |
| return false | |
| } | |
| reader.onload = (e) => { | |
| const attachment = new sforce.SObject('Attachment'); | |
| attachment.Name = file.name; | |
| attachment.IsPrivate = false; | |
| attachment.ContentType = file.type; | |
| attachment.Body = (new sforce.Base64Binary(e.target.result)).toString();; | |
| attachment.Description = file.name; | |
| attachment.ParentId = '{!parentId}'; //Where recordID is the ID of record to which you want to add your attachment | |
| const result = sforce.connection.create([attachment]) | |
| if (result[0].getBoolean("success")) { | |
| console.log('file added successfully') | |
| return true | |
| } else { | |
| console.error('error occured') | |
| return false | |
| } | |
| }; | |
| reader.onloadend = (e) => { | |
| const thumbReader = new FileReader() | |
| thumbReader.addEventListener("load", function() { | |
| const img = document.createElement('img') | |
| img.src = thumbReader.result | |
| img.style['max-width'] = '200px' | |
| img.style['max-height'] = '200px' | |
| //appendThumb(img) // could append here instead to show it was attached but my req is quick as poss. | |
| }, false); | |
| thumbReader.readAsDataURL(file); | |
| return true | |
| }; | |
| reader.readAsBinaryString(file); | |
| } | |
| /* SF UI FIX */ | |
| Array.from(document.querySelectorAll('button')).forEach(el => el.classList.remove('btn')) | |
| </script> | |
| </apex:page> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment