ShareChat
Moj

Simplifying Image Uploads on Web with Google Image Picker

Alok Singh and Amit Shukla

Alok Singh and Amit Shukla2 Jan, 2023

Follow us on FacebookFollow us on TwitterFollow us on InstagramFollow us on Linkedin
Simplifying Image Uploads on Web with Google Image Picker

Introduction

Google Image Picker is an npm plugin that helps you upload images directly from your web application without going through all the hassle of searching on your browser, downloading, and then uploading to the server.

Problem Statement

As we studied the issue, we realized that the entire process of searching the image in file manager, downloading and uploading is a tedious and time consuming task. At ShareChat, staying true to our value of ‘Speed’, we wanted to develop a method that helped us simplify this process and streamline it completely.

Solution

We developed a plugin that will allow users to directly upload images from the web application itself. For creating this plugin, we didn't use any framework-specific APIs and relied on Javascript's Mutation Observer API and Google Custom Search API, thus making this package independent of web frameworks.

Now let's talk about these two terms in detail.

Programmable Search Element Control API: The Programmable Search Element Control API lets you embed the Programmable Search Element in your web pages and other web applications using JavaScript.

Let's look at what this Programmable Search Element is.

The Programmable Search Element is a combination of two components,
i.e., search boxes and search results pages. There are different types of search elements available in the API

For Google Image Picker, we used the standard element type.
Programmable Search Elements come with a lot of customization like whether you want to open the result in a different window or enable autocomplete or not and many more. You just have to add queryParameterName attribute is prefixed with data-.
For Ex: <div class="gcse-search" data-newWindow="true">

You can use this list of supported attributes according to your needs.

Mutation Observer: This is a built-in JS object, it keeps track of DOM elements and fires a callback whenever a change is detected.

Now you have the definition, but how we use it is quite interesting, because Google only provides an overlay of image lists after the user searches any image over that. The entire overlay is dynamically injected in the DOM, so technically we do not have access to the images right away.

For Google Image Picker, we have to take control of the overlay as soon as the user searches for any image. For this, we use mutation observer, as mutation observer needs a node for observation, so we provided the element that is given via google customized search API <div class="gcse-search" data-disableWebSearch="true"></div>

So now whenever the user searches any image following things will happen:

  • Google result overlay will inject inside this div.
  • Mutation observer calls the callback.
  • Inside this callback we will attach click event listener to the Google result overlay.
  • Whenever the user clicks any image, we extract the hyperlink and convert it to a file and return this file.

Access API Key:

https://cse.google.com/cse.js?cx={ApiKey} here the Api key is basically cx value

You can access API Key by referring following link

Programmable Search Engine

Implementation:

Install @mohalla-tech/google-image-picker from npm

Add a div <div id='gcse-wrapper'></div> in your html where you want to use google-image-picker

Call fetchGoogleCSEScript({callback,apiKey,filename}) when the dom is mounted with an object argument consisting of three property -

  • callback function (mandatory)
  • apiKey string (mandatory)
  • fileName string (optional) if not provided then default value is image

When you click any of the images in the demo, the callback function will return an object (file: file; blob: string)

Demo

Link

Usage With Libraries/Framework

React:
Playground

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useEffect, useState } from 'react';
import { fetchGoogleCSEScript } from '@mohalla-tech/google-image-picker';

function App() {
  const [image, setImage] = useState(null);

  useEffect(() => {
    fetchGoogleCSEScript({
      callback: uploadImage,
      apiKey: <API_KEY>,
      fileNmae: 'notification',
    });
  }, []);

  const uploadImage = ({ file, blob }) => {
    setImage(blob);
  };
  return (
    <div>
      <div id="gcse-wrapper"></div>
      <img width="300" height="300" src={image} alt="" />
    </div>
  );
}

export default App;

Vue:
Playground

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
  <div id="app">
    <div id="gcse-wrapper"></div>
    <img width="300" height="300" :src="image" alt="" />
  </div>
</template>

<script>
import { fetchGoogleCSEScript } from "@mohalla-tech/google-image-picker";

export default {
  name: "App",
  data() {
    return {
      image: null,
    };
  },
  mounted() {
    fetchGoogleCSEScript({
      callback: this.uploadImage,
      apiKey: "a749032cf5",
      fileName: "notification",
    });
  },
  methods: {
    uploadImage({ file, blob }) {
      this.image = blob;
    },
  },
};
</script>

<style></style>

Solid JS:

Playground

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { onMount, createSignal } from 'solid-js';
import { fetchGoogleCSEScript } from '@mohalla-tech/google-image-picker';

function App() {
  const [image, setImage] = createSignal(null);

  onMount(() => {
    fetchGoogleCSEScript({
      callback: uploadImage,
      apiKey: 'a749032cf5',
      fileName: 'notification',
    });
  });
  const uploadImage = ({ file, blob }) => {
    setImage(blob);
  };

  return (
    <div>
      <div id="gcse-wrapper"></div>
      <img width="300" height="300" src={image()} alt="" />
    </div>
  );
}

export default App;

Svelte

Playground

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
  import { onMount } from "svelte";
  import { fetchGoogleCSEScript } from "@mohalla-tech/google-image-picker";
  let image = null;
  const uploadImage = ({ file, blob }) => {
    image = blob;
  };
  onMount(() => {
    fetchGoogleCSEScript({
      callback: uploadImage,
      apiKey: "a749032cf5",
      fileName: "notification",
    });
  });
</script>

<main>
  <div id="gcse-wrapper" />
  <img width="300" height="300" src={image} alt="" />
</main>

<style>
</style>

Other Suggested Blog

Are you in search of a job profile that fits your skill set perfectly?

Congratulations! You’ve reached the right place!

We are enroute to building a team of humble, yet ambitious folks. Grow professionally in your career with us, as we offer tremendous room for growth in unique career fields. Do not miss out on this unique opportunity. Send us your resume today!

\r\n\r\n ","_type":"code"},{"style":"normal","_key":"24d3bd6ef365","markDefs":[],"children":[{"_key":"ccb903a60bc80","_type":"span","marks":["strong"],"text":"Solid JS:"}],"_type":"block"},{"_type":"block","style":"normal","_key":"9299c6ff65af","markDefs":[{"href":"https://stackblitz.com/edit/solidjs-templates-psy3he?file=src%2FApp.jsx","_key":"a1f71208865e","_type":"link"}],"children":[{"marks":["a1f71208865e"],"text":"Playground","_key":"cd98148ea63a0","_type":"span"}]},{"language":"python","_key":"b8df68001624","code":"import { onMount, createSignal } from 'solid-js';\r\nimport { fetchGoogleCSEScript } from '@mohalla-tech/google-image-picker';\r\n\r\nfunction App() {\r\n const [image, setImage] = createSignal(null);\r\n\r\n onMount(() => {\r\n fetchGoogleCSEScript({\r\n callback: uploadImage,\r\n apiKey: 'a749032cf5',\r\n fileName: 'notification',\r\n });\r\n });\r\n const uploadImage = ({ file, blob }) => {\r\n setImage(blob);\r\n };\r\n\r\n return (\r\n
\r\n
\r\n \"\"\r\n
\r\n );\r\n}\r\n\r\nexport default App;","_type":"code"},{"markDefs":[],"children":[{"_type":"span","marks":["strong"],"text":"Svelte","_key":"8ff0444e72f20"}],"_type":"block","style":"normal","_key":"53ed36ec2250"},{"_key":"868b5ea7a9be","markDefs":[{"_key":"096496baea21","_type":"link","href":"https://stackblitz.com/edit/sveltejs-kit-template-default-dwwwzb?file=src/routes/+page.svelte"}],"children":[{"_type":"span","marks":["096496baea21"],"text":"Playground","_key":"33885c5f821c0"}],"_type":"block","style":"normal"},{"language":"python","_key":"7e3fad794799","code":"\r\n\r\n
\r\n
\r\n \"\"\r\n
\r\n\r\n ","_type":"code"}],"publishedAt":"2023-01-02T12:59:00.000Z","_id":"9fbc9ece-bf81-45f0-a1df-c0e44613c501","author":{"name":"Alok Singh and Amit Shukla","image":{"_type":"image","asset":{"_ref":"image-2cfb57475290f49f8f87fdcc8c69b8b2352ac208-735x752-jpg","_type":"reference"}}}},"nextBlogs":[{"_id":"00f9a035-f260-49f3-97fd-5e77bcb8954c","publishedAt":"2023-07-03T09:55:00.000Z","title":"From Struggle To Success: Civil Engineer Finds Global Fame On ShareChat","excerpt":"Mohit Arora, a motivated civil engineer, found peace and success on ShareChat, an innovative social networking platform, in a world where opportunities might be limited and goals seem out of reach.","mainImage":{"asset":{"url":"https://cdn.sanity.io/images/10qgadfo/production/da77415587f526c70c9f336f64ef4586fb2c7d40-1536x864.png","metadata":{"dimensions":{"height":864,"width":1536}}}},"author":{"name":"fibi","image":{"_type":"image","asset":{"_ref":"image-781ddf6d547d114f6cec1ce4625ae546914dc9a8-378x224-png","_type":"reference"}}},"slug":{"current":"from-struggle-to-success-civil-engineer-finds-global-fame-on-sharechat"},"categories":{"title":"In the News"}},{"slug":{"current":"four-interesting-sharechat-features-that-content-creators-should-try-out"},"categories":{"title":"In the News"},"_id":"012fb7ab-185e-4ba4-b6ca-499bc650b09d","publishedAt":"2022-08-19T07:34:00.000Z","title":"Four interesting ShareChat features that content creators should try out","excerpt":"ShareChat, which currently supports over 32 million Indian creators, offers several unique features that boost the creator journey and help them monetize their content","mainImage":{"asset":{"metadata":{"dimensions":{"width":1500,"height":800}},"url":"https://cdn.sanity.io/images/10qgadfo/production/4d823bb58ac9532b94ce995a7779a92c9a4cd1e2-1500x800.png"}},"author":{"name":"ShareChat","image":{"hotspot":{"width":0.7085427135678393,"x":0.49748743718592975,"y":0.3819095477386934,"height":0.7236180904522609,"_type":"sanity.imageHotspot"},"_type":"image","asset":{"_ref":"image-d6bc519d0bdd026d24b09ff5e7c43c5a5d0f6813-300x300-png","_type":"reference"},"crop":{"right":0,"top":0,"left":0,"bottom":0.20100502512562834,"_type":"sanity.imageCrop"}}}}],"cipherText":"U2FsdGVkX1/2fNFfnUfdn4/BMcD2NBhnRRRIKNJ2zkw=","cipherTextGenerationTime":1778433250454,"isSSR":true};