Last active
January 20, 2020 04:26
-
-
Save kevinrodriguez-io/ee4985be770e8deb89c2dfa82a8f4550 to your computer and use it in GitHub Desktop.
You can use the following hook to upload images, encode them and correct their orientation.
This file contains 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
yarn add blueimp-load-image |
This file contains 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
import LoadImage from 'blueimp-load-image' | |
/** | |
* @param {File} file | |
*/ | |
export const parseMetadata = file => { | |
return new Promise(resolve => { | |
LoadImage.parseMetaData(file, data => { | |
resolve(data) | |
}) | |
}) | |
} | |
/** | |
* @param {File} file | |
* @param {import('blueimp-load-image').LoadImageOptions} settings | |
* @returns {Promise<HTMLCanvasElement>} | |
*/ | |
export const loadImage = (file, settings) => { | |
return new Promise(resolve => { | |
LoadImage( | |
file, | |
/** @param {HTMLCanvasElement} canvas */ | |
canvas => resolve(canvas), | |
{ | |
...settings, | |
}, | |
) | |
}) | |
} | |
/** | |
* @param {HTMLCanvasElement} canvas | |
* @param {string} type | |
* @param {number} quality | |
* @returns {Promise<Blob>} | |
* */ | |
export const canvasToBlob = (canvas, type, quality) => { | |
return new Promise(resolve => { | |
canvas.toBlob(blob => resolve(blob), type, quality) | |
}) | |
} | |
/** | |
* @param {Blob} blob | |
* @returns {Promise<{dataUrl: string | ArrayBuffer, base64: string}>} | |
*/ | |
export const loadImageSrc = blob => { | |
return new Promise(resolve => { | |
const previewReader = new FileReader() | |
previewReader.onload = e => { | |
const dataUrl = e.target.result | |
// @ts-ignore | |
const [, base64] = dataUrl.split(',') | |
resolve({ | |
dataUrl, | |
base64, | |
}) | |
} | |
previewReader.readAsDataURL(blob) | |
}) | |
} |
This file contains 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
import { useState, useCallback } from 'react' | |
import { | |
parseMetadata, | |
loadImage, | |
canvasToBlob, | |
loadImageSrc, | |
} from './promises' | |
/** | |
* The useImageSelector hook allows for the creation and parsing of blob images selected from | |
* | |
* @param {Object} settings - Hook settings | |
* @param {string} [settings.imageMimeType] - Mime Type to be used | |
* @param {boolean} [settings.fixOrientation] - Fixes the orientation automatically from camera pictures | |
* @param {number} [settings.quality] - Image quality | |
* @param {import('blueimp-load-image').LoadImageOptions} [settings.loadImageOptions] - Image loader options | |
* @returns {[boolean, any, Blob, String | ArrayBuffer, string, ({ target }: React.ChangeEvent<HTMLInputElement>) => Promise<void>]} | |
*/ | |
const useImageSelector = ({ | |
imageMimeType = 'image/jpeg', | |
quality = 70, | |
fixOrientation = true, | |
loadImageOptions = { | |
canvas: true, | |
maxWidth: 500, | |
}, | |
} = {}) => { | |
const [isLoading, setIsLoading] = useState(false) | |
const [error, setError] = useState(null) | |
const [blob, setBlob] = useState(null) | |
const [base64Contents, setBase64Contents] = useState(null) | |
const [imageSrc, setImageSrc] = useState(null) | |
const onImageFileSelected = useCallback( | |
/*** | |
* @param {React.ChangeEvent<HTMLInputElement>} event | |
* */ | |
async ({ target }) => { | |
setIsLoading(true) | |
try { | |
const file = target.files.item(0) | |
const meta = await parseMetadata(file) | |
const options = fixOrientation | |
? { | |
orientation: meta.exif ? meta.exif.get('Orientation') : 0, | |
...loadImageOptions, | |
} | |
: loadImageOptions | |
const canvas = await loadImage(file, options) | |
const blob = await canvasToBlob(canvas, imageMimeType, quality) | |
const { dataUrl, base64 } = await loadImageSrc(blob) | |
setBlob(blob) | |
setImageSrc(dataUrl) | |
setBase64Contents(base64) | |
} catch (error) { | |
setError(error) | |
} finally { | |
setIsLoading(false) | |
} | |
}, | |
[fixOrientation, imageMimeType, loadImageOptions, quality], | |
) | |
return [isLoading, error, blob, base64Contents, imageSrc, onImageFileSelected] | |
} | |
export default useImageSelector |
This file contains 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
import React from 'react' | |
import useImageSelector from 'useImageSelector' | |
const IMAGE_SELECTOR_SETTINGS = Object.freeze({ | |
imageMimeType: 'image/png', | |
quality: 70, | |
fixOrientation: true, | |
loadImageOptions: Object.freeze({ | |
canvas: true, | |
maxWidth: 500, | |
}), | |
}) | |
const MyComponent = () => { | |
const [ | |
isLoadingImage, | |
imageError, | |
blob, // Binary Format | |
base64Contents, // Raw Base64 contents | |
imageSrc, // Image source to be used in HTML | |
onImageFileSelected, // Callback to be used by a type="file" input | |
] = useImageSelector(IMAGE_SELECTOR_SETTINGS) | |
return ( | |
<div> | |
{imageSrc && ( | |
<section className="previewer-container"> | |
<img className="previewer-container__image" src={imageSrc} /> | |
</section> | |
)} | |
{imageError && <pre>{JSON.stringify({ imageError }, null, 2)}</pre>} | |
<input | |
style={{ | |
position: 'absolute', | |
display: 'none', | |
opacity: 0; | |
}} | |
id="inputImage" | |
type="file" | |
accept="image/*" | |
onChange={onImageFileSelected} | |
/> | |
<label htmlFor="inputImage"> | |
Upload an Image | |
</label> | |
</div> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment