How to upload files to Google Cloud Storage using Javascript on only the browser
Tags: javascript, webdev, googlecloud, programming
Published: 2022-05-09 04:08:26

This article shows how to upload files to Google Cloud Storage using Javascript on only the browser (without Node.js).

# Implementation
This source code was made based on [GCP(Google Cloud Platform) Official reference](https://cloud.google.com/storage/docs/authentication). This article uses JSON API GCP provides and OAuth2.0.

## Auth (OAuth2.0)
The goal in this section is to get an access token to access Cloud Storage. We get it through Google OAuth2.0. ["OAuth 2.0 for Client-side Web Applications"](https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow?hl=en) shows us the necessary source code for authorization. This is a reference to make the following source code.

### Get Necessary Information 
You need to get the necessary information to use Cloud Storage through JSON API. These are the following three items.

- Client ID
- Redirect URL
- Scope

Please refer to ["How to get Google Client ID and Client Secret?"](https://www.balbooa.com/gridbox-documentation/how-to-get-google-client-id-and-client-secret) if you do not have any idea to get them. Additionally, please make the "OAuth consent screen." Please select the appropriate scope according to ["
Cloud Storage authentication"](https://cloud.google.com/storage/docs/authentication).

### Get Access Token (Source Code and Result)
This section shows the source code that gets access token only. It provides the function to get an access token through the OAuth consent screen when the user clicks the "Get Access Token" button.

#### Source Code

**index.html**
```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="text/javascript" src="/index.js" defer></script>
  </head>
  <body>
    <button onclick="oauthSignIn()">Get Access Token</button>
    <form id="formElem">
      <input type="file" id="file-upload" name="file-upload" />
      <input type="submit" id="submit-button" disabled />
    </form>
  </body>
</html>
```

**index.js**
```js
/**
 * Extract the access token from the URL
 */
for (value of location.hash.split("&")) {
  if (value.indexOf("access_token=") === 0) {
    const access_token = value.split("=")[1];
    const ele = document.createElement("p");
    ele.textContent = "Your access token is '" + access_token + "'.";
    document.body.appendChild(ele);
  }
}
/**
 * The following source code is from the page.
 * https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#oauth-2.0-endpoints
 */
/*
 * Create a form to request an access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = "https://accounts.google.com/o/oauth2/v2/auth";

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement("form");
  form.setAttribute("method", "GET"); // Send as a GET request.
  form.setAttribute("action", oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {
    client_id: [Client ID you set up before section],
    redirect_uri: [Refirect URL you set up before section, for example, "http://localhost:3000"],
    response_type: "token",
    scope: [Scope you set up before section, for example, "https://www.googleapis.com/auth/devstorage.read_write"],
    include_granted_scopes: "true",
    state: "pass-through value",
  };

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement("input");
    input.setAttribute("type", "hidden");
    input.setAttribute("name", p);
    input.setAttribute("value", params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

```

#### Result

![Get the access token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mds4sv34xrwholdfa5wt.png)
 
### Upload Files to Cloud Storage (Source Code and Result)
This section shows how to upload files to Cloud Storage using the access token you get in the previous section. You need to make a new bucket on Cloud Console. If you don't know how to make it, please refer to ["Create storage buckets"](https://cloud.google.com/storage/docs/creating-buckets#storage-create-bucket-console).

#### Source Code
The HTML file is the same as before. Please fix the Javascript source code as the following.

**index.js**
```js
const bucketName = [Bucket name you made];
const clientID = [Client ID you set up];
const redirectURL = [Redirect URL];
const scope = [Scope];
let accessToken = null;

/**
 * Extract the access token from the redirect URL
 */
for (value of location.hash.split("&")) {
  if (value.indexOf("access_token=") === 0) {
    const access_token = value.split("=")[1];
    const ele = document.createElement("p");
    ele.textContent = "Your access token is '" + access_token + "'.";
    accessToken = access_token;
    document.body.appendChild(ele);
    document.getElementById("submit-button").disabled = false;
  }
}

/**
 * Upload file
 */
formElem.onsubmit = async (e) => {
  e.preventDefault();
  const file = document.getElementById("file-upload");
  const filename = file.value.split("\\").slice(-1)[0];
  console.log("File Name: " + JSON.stringify(filename));
  const extension = filename.split(".").slice(-1)[0].toLocaleLowerCase();
  let contentType = null;
  // Detect Content Type
  if (extension === "png") {
    contentType = "image/png";
  } else if (extension === "jpg" || extension === "jpeg") {
    contentType = "image/jpeg";
  } else if (extension === "svg") {
    contentType = "image/svg+xml";
  } else if (extension === "mpeg") {
    contentType = "video/mpeg";
  } else if (extension === "webm") {
    contentType = "video/webm";
  } else {
    alert("This file is invalid.");
  }
  // Load file and upload it
  if (contentType) {
    const reader = new FileReader();
    reader.addEventListener("load", async (event) => {
      const bytes = event.target.result;
      let response = await fetch(
        `https://storage.googleapis.com/upload/storage/v1/b/${bucketName}/o?uploadType=media&name=${filename}`,
        {
          method: "POST",
          headers: {
            "Content-Type": contentType,
            Authorization: `Bearer ${accessToken}`,
          },
          body: bytes,
        }
      );

      let result = await response.json();
      if (result.mediaLink) {
        alert(
          `Success to upload ${filename}. You can access it to ${result.mediaLink}`
        );
      } else {
        alert(`Failed to upload ${filename}`);
      }
    });

    reader.readAsArrayBuffer(file.files[0]);
  }
};

/**
 * The following Source code originated from https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#oauth-2.0-endpoints
 */
/*
 * Create a form to request an access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = "https://accounts.google.com/o/oauth2/v2/auth";

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement("form");
  form.setAttribute("method", "GET"); // Send as a GET request.
  form.setAttribute("action", oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {
    client_id: clientID,
    redirect_uri: redirectURL,
    response_type: "token",
    scope: scope,
    include_granted_scopes: "true",
    state: "pass-through value",
  };

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement("input");
    input.setAttribute("type", "hidden");
    input.setAttribute("name", p);
    input.setAttribute("value", params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}
```

#### Result

![The result to upload file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxxwz6h6nhw1neasiqmk.png)

I list references in the following.

- MIME Types

{% embed https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types %}

- upload file to Cloud Storage using `curl`

{% embed https://cloud.google.com/storage/docs/uploading-objects#rest-upload-objects %}

- `fetch` usage

{% embed https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch %}

- `<input type="file">` usage
{% embed https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications#accessing_selected_files %}
{% embed https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file %}

---

The original article is the following. This article was translated from Japanese to English.
{% embed https://zenn.dev/akira_kashihara/articles/a7c0c0df48f5a0 %}