Encountering a 403 error when attempting to upload files to Google Cloud Storage (GCS) using Signed URLs

The main aim is to create a signed URL in the api/fileupload.js file for uploading the file to GCS. Then, retrieve the signed URL from the Nextjs server through the nextjs API at localhost://3000/api/fileupload. Finally, use the generated signed URL to upload the file to GCS in the index.jsx file.

Although the signed URL is successfully generated, when attempting to upload the image body as form data to GCS, a 403 error code occurs. Here is the response body:

body : (...)
bodyUsed : false
headers : 
Headers {}
ok : false
redirected : false
status : 0
statusText : ""
type : "opaque"
url : ""

Am I following the correct method for uploading files as form data in the index.jsx file? What could be missing here?

Below are the two files mentioned:

index.jsx for the Nextjs file:

    
    import { useState } from "react";

    export default function Home() {
    ...

fileupload.js in the api/ folder:

import { Storage } from "@google-cloud/storage";
...

Answer №1

I needed to extract the name of the original file in order to generate a signed URL and then retrieve that signed URL using the Nextjs GET API.

Below is the code solution - in api/fileupload.ts

import { Storage } from "@google-cloud/storage";
import type { NextApiRequest, NextApiResponse } from "next";
const storage = new Storage({
  keyFilename: `service_account_key.json`,
  projectId: "my-project-id"
});
const bucketName = "bucket-name";

export default async function handler(
  req: NextApiRequest & { files?: any },
  res: NextApiResponse<any>
) {
  const options = {
    version: "v4",
    action: "write",
    expires: Date.now() + 15 * 60 * 1000 // 15 minutes
    // contentType: "application/octet-stream"
  } as any;

  const newFileName = req.query.name as string;
  const file = storage.bucket(bucketName).file(newFileName);
  const [url]: any = await file.getSignedUrl(options);

  console.log("Generated PUT signed URL:", url);
  res.status(200).json({ url: url });
}

The signed URL is obtained through the Nextjs GET API, which is then used to call a PUT API with the signed URL, data extracted from the target object of the event, and the actual content type specified in the header.

index.jsx file -

import { useState } from "react";
import axios from "axios";
import Image from "next/image";
import Link from "next/link";
import { FiExternalLink } from "react-icons/fi";
import Loader from "./Loader";
export default function Home() {
  const [url, setUrl] = useState("");
  const [file, setFile] = useState<any>(null);
  const [dataloaded, setDataloaded] = useState(true);
  const [fileUploadDone, setFileUploadDone] = useState(false);
  
  
  const handleSubmit = async (e: any) => {
    setDataloaded(false);
    e.preventDefault();
    const response = await fetch(`/api/fileupload?name=${file.data.name}`, {
      method: "GET"
    });
    const responseWithBody = await response.json();
    console.log(responseWithBody.url);

    if (response.status === 200) {
      setUrl(responseWithBody.url);
    } else {
      console.log("error in generating url");
    }
    const response1 = await axios.put(responseWithBody.url, file.data, {
      headers: {
        "Content-Type": `${file.data.type}`
      }
    });
    if (response1.status === 200) {
      setFileUploadDone(true);
    } else {
    }
    setDataloaded(true);
    console.log(response1, file.data.type);
  };
  const handleFileChange = (e: any) => {
    const img = {
      preview: URL.createObjectURL(e.target.files[0]),
      data: e.target.files[0]
    };
    setFile(img);
  };

  return (
    <>
      <div className="form-container">
        <form onSubmit={handleSubmit}>
          <div className="image-preview-container">
            {file ? (
              <Image
                width={"400"}
                height={"400"}
                src={file.preview}
                alt="File to upload"
              />
            ) : (
              <Image
                width={"400"}
                height={"400"}
                src="https://raw.githubusercontent.com/koehlersimon/fallback/master/Resources/Public/Images/placeholder.jpg"
                alt="Fallback"
              />
            )}
          </div>
          <div className="file-name">
            {file && file.data.name}
           
          </div>
          <input
            type="file"
            name="file"
            onChange={handleFileChange}
            className="custom-file-input"
          ></input>
          <button
            className="submit-button"
            type="submit"
            disabled={!file}
            onClick={handleSubmit}
          >
            Submit
          </button>
          {fileUploadDone && (
            <span style={{ marginTop: "20px" }}>
              File upload is done successfully.{" "}
              <span
                onClick={() => {
                  setFileUploadDone(false);
                  setFile(null);
                  setDataloaded(true);
                }}
              >
                Click to Upload Again
              </span>
            </span>
          )}
        </form>
      </div>
      {!dataloaded && <Loader />}
    </>
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An issue has occurred: [ERR_REQUIRE_ESM] You must utilize the import statement in order to load an

These are the snippets of code I'm working with: index.ts import { MikroORM } from "@mikro-orm/core" import { __prod__ } from "./constants"; import { Post } from "./entities/Post"; import microConfig from "./mikro-o ...

Specify the data type of a nested object in a React component with TypeScript

Interface Button{ buttonTitle: { name?: string; } } What is the best way to specify a type for the buttonTitle property? ...

Nestjs RabbitMq Microservices

I'm currently developing a microservice that is responsible for receiving messages from RabbitMQ. However, I am encountering an error message as follows: ERROR [Server] There is no matching event handler defined in the remote service. Event pattern: u ...

Passing data from ModalService to a component

Currently, I am attempting to utilize the ngx-bootstrap-modal in order to transfer data from a modal service to a modal component. While reviewing the examples, it is suggested to use the following code: this.modalService.show(ModalContentComponent, {init ...

"Looking to log in with NextAuth's Google Login but can't find the Client Secret

I am attempting to create a login feature using Next Auth. All the necessary access data has been provided in a .env.local file. Below are the details: GOOGLE_CLIENT_ID=[this information should remain private].apps.googleusercontent.com GOOGLE_CLIENT_SECR ...

Cordova's FileReader takes precedence over TypeScript's FileReader functionality

I encountered an issue when adding the cordova-plugin-file-transfer plugin, where I received the error message: reader.addEventListener is not a function. This problem arises due to Cordova FileReader class overriding typescript FileReader. How can this ...

Create a new function and assign it to "this" using the button inside ngFor loop

I am working with a li tag that has a *ngFor directive: <li *ngFor="let items of buttons"> <button (click)="newMap(items.id, $event)"> {{ items.name }} </button> </li> The buttons array looks like this: buttons = [ {nam ...

How to set the type of an object property to a string based on a string from an array of strings in TypeScript

Great { choices: ['Bob', 'Chris', 'Alice'], selectedChoice: 'Alice', } Not So Good { choices: ['Bob', 'Chris', 'Alice'], selectedChoice: 'Sam', } I've been exp ...

The React-Big-Calendar Drag and Drop feature in the month view consistently drags events from the leftmost column

I'm seeking assistance with a bug I've encountered while using the big-react-calendar. The issue arises when dragging an event, as it consistently moves to the leftmost column regardless of mouse position. However, shifting the event to a differe ...

Angular - The differ is unable to find support for the object 'Item One' which is of type 'string'. NgFor is only able to bind to Iterables like Arrays and not individual objects

Many questions on StackOverflow share similarities with this one, but I have not been able to find a solution that fits my issue. My code functions correctly when attempting to populate items in a "Select" control. It successfully retrieves data if the it ...

Steps to allow an ng-model to accept a variety of input values

Imagine having an input box structured like this <ion-input [(ngModel)]="Gender" type="text" placeholder="Gender Type"></ion-input> <ion-input [(ngModel)]="hairCat" type="text" placeholder="Hair Type"></ion-input> Now, let's ...

Angular 14 presents an issue where the injectable 'PlatformLocation' requires compilation with the JIT compiler; however, the '@angular/compiler' module is currently missing

I've encountered the following error and have tried multiple solutions, but none of them have been successful: Error: The injectable 'PlatformLocation' requires JIT compilation with '@angular/compiler', which is not available. ...

Obtain headers from an Excel file uploaded using the XLSX format in Angular

Is there a way to extract the first row containing name, email, and mobile as an array from an uploaded excel file? I am currently utilizing XLSX for this purpose. While I am able to retrieve the entire dataset into an array, my main goal is to only ex ...

Watching the Event Emitters emitted in Child Components?

How should we approach utilizing or observing parent @Output() event emitters in child components? For instance, in this demo, the child component utilizes the @Output onGreetingChange as shown below: <app-greeting [greeting]="onGreetingChange | a ...

User interaction with a checkbox triggers a state change in Angular

Here is the code snippet I am working with, where clicking should set the checked value to false: @Component({ template: ` <input type="checkbox" [checked]="checked" (change)="onChange()"> ` }) export class AppC ...

The typescript error TS2339 is triggered by the property 'webkitURL' not being found on the 'Window' type

Currently utilizing Angular 2 in a project that is compiled with TypeScript. Encountering an issue when attempting to generate a blob image: Error TS2339: Property 'webkitURL' does not exist on type 'Window' The TypeScript code causi ...

What is the process for refreshing one observable with data from another observable?

As a complete beginner to these technologies, please bear with me if my question sounds strange and the terminology is not quite right. I have a component that displays data in a table format. export class SourceFieldComponent implements OnInit { ... ...

When using the e.target.getAttribute() method in React, custom attributes may not be successfully retrieved

I am struggling with handling custom attributes in my changeHandler function. Unfortunately, React does not seem to acknowledge the custom "data-index" attribute. All other standard attributes (such as name, label, etc.) work fine. What could be the issu ...

What is the best way to incorporate Typescript React Components across various projects?

I'm venturing into developing an npm package that involves several react components implemented with typescript. As a newcomer to react and npm, I apologize if my query seems basic. Despite researching online, there isn't much information on this ...

Maximum Age Setting for Iron Session Cookie

Currently, I am attempting to configure a Next JS application with iron-session and a 'remember me' feature. The objective is for the maxAge of the iron-session cookie to be extended to a week if the user selects the remember me option on the log ...