{"id":4592,"date":"2023-07-24T04:06:51","date_gmt":"2023-07-24T04:06:51","guid":{"rendered":"https:\/\/rengga.dev\/blog\/?p=4592"},"modified":"2023-08-28T23:47:45","modified_gmt":"2023-08-28T23:47:45","slug":"build-an-image-compressor-using-react","status":"publish","type":"post","link":"https:\/\/rengga.dev\/blog\/build-an-image-compressor-using-react\/","title":{"rendered":"Build an Image Compressor using React"},"content":{"rendered":"<figure id=\"attachment_4597\" aria-describedby=\"caption-attachment-4597\" style=\"width: 1384px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/rengga.dev\/demo\/react-image-compressor\/\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-4597 size-full\" src=\"https:\/\/rengga.dev\/blog\/wp-content\/uploads\/2023\/06\/Build-an-Image-Compressor-using-React.png\" alt=\"Build an Image Compressor using React\" width=\"1384\" height=\"609\" srcset=\"https:\/\/rengga.dev\/blog\/wp-content\/uploads\/2023\/06\/Build-an-Image-Compressor-using-React.png 1384w, https:\/\/rengga.dev\/blog\/wp-content\/uploads\/2023\/06\/Build-an-Image-Compressor-using-React-768x338.png 768w\" sizes=\"auto, (max-width: 1384px) 100vw, 1384px\" \/><\/a><figcaption id=\"caption-attachment-4597\" class=\"wp-caption-text\">Build an Image Compressor using React<\/figcaption><\/figure>\n<p>Images draw attention, trigger emotions, and improve the user experience of your website. Image compression involves the reduction of the size of your image so that it takes less space on the hard drive and for better search engine optimization when it comes to websites e.g., 2MB to 440kbs. The image still retains its physical properties. We are going to use React to compress our images.<\/p>\n<p>&nbsp;<\/p>\n<div id=\"pre_requisites\" class=\"c-heading-permalink c-heading-permalink--h2 js-heading-permalink \">\n<h2 class=\"c-heading-permalink__heading\">Pre-requisites<\/h2>\n<p>To effectively follow along through this article you are required to have:<\/p>\n<\/div>\n<ul>\n<li>React.js basic skills<\/li>\n<li>Basic knowledge of HTML<\/li>\n<li>Knowledge of Javascript<\/li>\n<li>Some knowledge of Bootstrap<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Features<\/h2>\n<ul>\n<li>You can use this module to compress jpeg, png, webp, and bmp images by reducing\u00a0<strong>resolution<\/strong>\u00a0or\u00a0<strong>storage size<\/strong>\u00a0before uploading to the application server to save bandwidth.<\/li>\n<li><strong>Multi-thread<\/strong>\u00a0(web worker) non-blocking compression is supported through options.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Demo \/ Example<\/h2>\n<p>open <a href=\"https:\/\/rengga.dev\/demo\/react-image-compressor\/\" target=\"_blank\" rel=\"noopener\"><strong>https:\/\/rengga.dev\/demo\/react-image-compressor\/<\/strong><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>Usage<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">&lt;input type=\"file\" accept=\"image\/*\" onchange=\"handleImageUpload(event);\"&gt;<\/pre>\n<h3><\/h3>\n<h3>async await syntax:<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">async function handleImageUpload(event) {\r\n\r\n  const imageFile = event.target.files[0];\r\n  console.log('originalFile instanceof Blob', imageFile instanceof Blob); \/\/ true\r\n  console.log(`originalFile size ${imageFile.size \/ 1024 \/ 1024} MB`);\r\n\r\n  const options = {\r\n    maxSizeMB: 1,\r\n    maxWidthOrHeight: 1920,\r\n    useWebWorker: true,\r\n  }\r\n  try {\r\n    const compressedFile = await imageCompression(imageFile, options);\r\n    console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); \/\/ true\r\n    console.log(`compressedFile size ${compressedFile.size \/ 1024 \/ 1024} MB`); \/\/ smaller than maxSizeMB\r\n\r\n    await uploadToServer(compressedFile); \/\/ write your own logic\r\n  } catch (error) {\r\n    console.log(error);\r\n  }\r\n\r\n}<\/pre>\n<h3><\/h3>\n<h3>Promise.then().catch() syntax:<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">function\u00a0handleImageUpload(event)\u00a0{\r\n\r\n\u00a0\u00a0var\u00a0imageFile\u00a0=\u00a0event.target.files[0];\r\n\u00a0\u00a0console.log('originalFile\u00a0instanceof\u00a0Blob',\u00a0imageFile\u00a0instanceof\u00a0Blob);\u00a0\/\/\u00a0true\r\n\u00a0\u00a0console.log(`originalFile\u00a0size\u00a0${imageFile.size\u00a0\/\u00a01024\u00a0\/\u00a01024}\u00a0MB`);\r\n\r\n\u00a0\u00a0var\u00a0options\u00a0=\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0maxSizeMB:\u00a01,\r\n\u00a0\u00a0\u00a0\u00a0maxWidthOrHeight:\u00a01920,\r\n\u00a0\u00a0\u00a0\u00a0useWebWorker:\u00a0true\r\n\u00a0\u00a0}\r\n\u00a0\u00a0imageCompression(imageFile,\u00a0options)\r\n\u00a0\u00a0\u00a0\u00a0.then(function\u00a0(compressedFile)\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0console.log('compressedFile\u00a0instanceof\u00a0Blob',\u00a0compressedFile\u00a0instanceof\u00a0Blob);\u00a0\/\/\u00a0true\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0console.log(`compressedFile\u00a0size\u00a0${compressedFile.size\u00a0\/\u00a01024\u00a0\/\u00a01024}\u00a0MB`);\u00a0\/\/\u00a0smaller\u00a0than\u00a0maxSizeMB\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return\u00a0uploadToServer(compressedFile);\u00a0\/\/\u00a0write\u00a0your\u00a0own\u00a0logic\r\n\u00a0\u00a0\u00a0\u00a0})\r\n\u00a0\u00a0\u00a0\u00a0.catch(function\u00a0(error)\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0console.log(error.message);\r\n\u00a0\u00a0\u00a0\u00a0});\r\n}\r\n\r\n<\/pre>\n<h3><\/h3>\n<h3><\/h3>\n<h2>Installing<\/h2>\n<h3>Use as ES module:<\/h3>\n<p>You can install it via npm or yarn<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">npm install browser-image-compression --save\r\n# or\r\nyarn add browser-image-compression<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">import imageCompression from 'browser-image-compression';<\/pre>\n<p>(can be used in frameworks like React, Angular, Vue etc)<\/p>\n<p>(work with bundlers like webpack and rollup)<\/p>\n<h3><\/h3>\n<h3><a id=\"user-content-or-load-umd-js-file\" class=\"anchor\" href=\"https:\/\/www.npmjs.com\/package\/browser-image-compression#or-load-umd-js-file\" aria-hidden=\"true\"><\/a>(or) Load UMD js file:<\/h3>\n<p>You can download imageCompression from the\u00a0dist folder.<\/p>\n<p>Alternatively, you can use a CDN like\u00a0<a href=\"https:\/\/cdn.jsdelivr.net\/\" rel=\"nofollow\">delivrjs<\/a>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">&lt;script type=\"text\/javascript\" src=\"https:\/\/cdn.jsdelivr.net\/npm\/browser-image-compression@2.0.1\/dist\/browser-image-compression.js\"&gt;&lt;\/script&gt;<\/pre>\n<h3><\/h3>\n<h3><\/h3>\n<h2>API<\/h2>\n<h3><a id=\"user-content-main-function\" class=\"anchor\" href=\"https:\/\/www.npmjs.com\/package\/browser-image-compression#main-function\" aria-hidden=\"true\"><\/a>Main function<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\/\/ you should provide one of maxSizeMB, maxWidthOrHeight in the options\r\nconst options: Options = { \r\n  maxSizeMB: number,            \/\/ (default: Number.POSITIVE_INFINITY)\r\n  maxWidthOrHeight: number,     \/\/ compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)\r\n                                \/\/ but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.\r\n                                \/\/ Please check the Caveat part for details.\r\n  onProgress: Function,         \/\/ optional, a function takes one progress argument (percentage from 0 to 100) \r\n  useWebWorker: boolean,        \/\/ optional, use multi-thread web worker, fallback to run in main-thread (default: true)\r\n  libURL: string,               \/\/ optional, the libURL of this library for importing script in Web Worker (default: https:\/\/cdn.jsdelivr.net\/npm\/browser-image-compression\/dist\/browser-image-compression.js)\r\n  preserveExif: boolean,        \/\/ optional, use preserve Exif metadata for JPEG image e.g., Camera model, Focal length, etc (default: false)\r\n\r\n  signal: AbortSignal,          \/\/ optional, to abort \/ cancel the compression\r\n\r\n  \/\/ following options are for advanced users\r\n  maxIteration: number,         \/\/ optional, max number of iteration to compress the image (default: 10)\r\n  exifOrientation: number,      \/\/ optional, see https:\/\/stackoverflow.com\/a\/32490603\/10395024\r\n  fileType: string,             \/\/ optional, fileType override e.g., 'image\/jpeg', 'image\/png' (default: file.type)\r\n  initialQuality: number,       \/\/ optional, initial quality value between 0 and 1 (default: 1)\r\n  alwaysKeepResolution: boolean \/\/ optional, only reduce quality, always keep width and height (default: false)\r\n}\r\n<\/pre>\n<h4>Caveat<\/h4>\n<p>Each browser limits\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/canvas#maximum_canvas_size\" rel=\"nofollow\">the maximum size<\/a>\u00a0of a browser Canvas object.<br \/>\nSo, we resize the image to less than the maximum size that each browser restricts.<br \/>\n(However, the\u00a0<code>proportion\/ratio<\/code>\u00a0of the image remains.)<\/p>\n<h4><a id=\"user-content-abort--cancel-compression\" class=\"anchor\" href=\"https:\/\/www.npmjs.com\/package\/browser-image-compression#abort--cancel-compression\" aria-hidden=\"true\"><\/a>Abort \/ Cancel Compression<\/h4>\n<p>To use this feature, please check the browser compatibility:\u00a0<a href=\"https:\/\/caniuse.com\/?search=AbortController\" rel=\"nofollow\">https:\/\/caniuse.com\/?search=AbortController<\/a><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">function handleImageUpload(event) {\r\n\r\n  var imageFile = event.target.files[0];\r\n\r\n  var controller = new AbortController();\r\n\r\n  var options = {\r\n    \/\/ other options here\r\n    signal: controller.signal,\r\n  }\r\n  imageCompression(imageFile, options)\r\n    .then(function (compressedFile) {\r\n      return uploadToServer(compressedFile); \/\/ write your own logic\r\n    })\r\n    .catch(function (error) {\r\n      console.log(error.message); \/\/ output: I just want to stop\r\n    });\r\n  \r\n  \/\/ simulate abort the compression after 1.5 seconds\r\n  setTimeout(function () {\r\n    controller.abort(new Error('I just want to stop'));\r\n  }, 1500);\r\n}<\/pre>\n<h3><\/h3>\n<h3>Helper function<\/h3>\n<ul>\n<li>for advanced users only, most users won&#8217;t need to use the helper functions<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Contribution<\/h2>\n<ol>\n<li>fork the repo and git clone it<\/li>\n<li>run\u00a0<code>npm run watch<\/code>\u00a0# it will watch code change in lib\/ folder and generate js in dist\/ folder<\/li>\n<li>add\/update code in lib\/ folder<\/li>\n<li>try the code by opening example\/development.html which will load the js in dist\/ folder<\/li>\n<li>add\/update test in test\/ folder<\/li>\n<li><code>npm run test<\/code><\/li>\n<li>push to your forked repo on github<\/li>\n<li>make a pull request to dev branch of this repo<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Images draw attention, trigger emotions, and improve the user experience of your <a class=\"read-more\" href=\"https:\/\/rengga.dev\/blog\/build-an-image-compressor-using-react\/\" title=\"Build an Image Compressor using React\" itemprop=\"url\"><\/a><\/p>\n","protected":false},"author":1,"featured_media":4376,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[508,12],"tags":[667,672,668,670,671,669,666],"newstopic":[578],"class_list":{"0":"post-4592","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-react","8":"category-web-development","9":"tag-image-compression","10":"tag-image-compression-full-source-code","11":"tag-image-processing","12":"tag-react-app","13":"tag-react-image-compressor","14":"tag-reduce-resolution","15":"tag-reduce-size","16":"newstopic-react"},"_links":{"self":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/4592","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/comments?post=4592"}],"version-history":[{"count":7,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/4592\/revisions"}],"predecessor-version":[{"id":4604,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/4592\/revisions\/4604"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media\/4376"}],"wp:attachment":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media?parent=4592"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/categories?post=4592"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/tags?post=4592"},{"taxonomy":"newstopic","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/newstopic?post=4592"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}