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

Upon the initial rendering, Next.js obtains access to the query { amp: undefined }

When using Next.js 9.3, I encountered an issue where I needed to access the query on initial render and pass the result of the query to next/head in order to change the title and description of the page. The component is receiving the query from the useRo ...

Is there a way to display an array of data in separate mat-form-field components?

I am dealing with an array that stores 4 data points: [onHour, onMinute, offHour, offMinute]. I also have 4 elements that are not in an array and need to be repeated. <div class="on"> <mat-form-field appeara ...

Error message encountered when using React Hook Form: "Unable to display errors due to TypeError - cannot read properties of undefined, specifically 'name'"

I'm facing an issue with React Hook Form v7 where it fails to recognize error messages, resulting in the following error: TypeError: Cannot read properties of undefined (reading 'name') for every input field. It seems like the form doesn&ap ...

Unable to locate any static exports within the TypeScript library bundle

In my file Style.ts, I have a class called Style: export class Style { ... } The Style class consists of properties, methods, and a constructor, along with import statements for other class dependencies. It is being used by other classes through the ...

After creating my Docker container, it is unable to detect any new files within its assigned volume

I am encountering an issue with my Nextjs app and images in my docker-compose file on my Ubuntu VPM with bind volumes. After composing the website, I am unable to view any new files that are uploaded; only the ones that were in /root/docker/public at the t ...

I'm having trouble with TypeScript locating a method specified in a parent class's type definition. What might be causing this issue?

While working with JointJS, I came across the defined typings. In my Typescript class, the structure is as follows: namespace joint { namespace shapes { namespace devs { class Model extends basic.Generic { ... } } } } ...

Trouble with Transitioning Material UI from version 4 to version 5

I have been working on upgrading my NextJS project from Material UI v4 to v5 by following the official guide available at: https://mui.com/guides/migration-v4/ However, after meticulously following all the steps outlined in the guide, I encountered a frus ...

Ways to acquire dynamic content without relying on Remark/Grey Matter

In my stack of technologies, I am using nextJS with react and typescript. While I have successfully set dynamic routes for my blog posts, I am now facing a challenge in creating pages that do not rely on markdown. Despite searching extensively for code exa ...

The CldUploadWidget creates an overlay featuring a never-ending loading spinner

Attempting to utilize the CldUploadWidget from the next-cloudinary package in a Next.js app with the app router. The goal is to upload an image and retrieve the public_id and URL, but upon setup, clicking the button that opens the overlay only displays a l ...

If an Angular reactive form component has a particular value

I am working with a group of radio buttons. When a user chooses the option "yes," I would like to display an additional input box on the form. Link to Code Example HTML.component <div formGroupName="radioButtonsGroup" class="form-group col-6 pl-0 pt- ...

deploy a React application on a server running Nginx, utilizing port 443

We are currently running a React project on port 3000 using the 'yarn start' command. Our goal now is to implement SSL in order to access the project from port 443 instead of having port 3000 displayed in the URL. We attempted using the proxy pas ...

Utilizing Next.js and React to interact with Open AI through API requests

I'm currently experimenting with the OpenAI API to develop a chatbot using React, TypeScript, and Next.js. I am facing an issue where clicking the send button in the UI does not trigger any action. Even after adding console.log statements, nothing sho ...

Trigger on the cancellation or completion of a nested observable

I'm seeking a way to detect if an inner observable was not successfully completed (due to everyone unsubscribing) and then emit a value in that scenario. Something akin to defaultIfEmpty, but the current solution isn't effective. A trigger exis ...

What steps can I take to ensure that the elements are in the same row instead of being displayed in three separate rows?

(I'm a beginner in web development and need some help) Is there a way to align elements into the same row instead of stacking them up in separate rows? I'm working on creating a header bar similar to the one on the Naive UI Documentation Website. ...

Problem with MongoDB - increasing number of connections

I have encountered an issue with my current approach to connecting to MongoDB. The method I am using is outlined below: import { Db, MongoClient } from "mongodb"; let cachedConnection: { client: MongoClient; db: Db } | null = null; export asyn ...

Using object bracket notation in TypeScript to retrieve values from props with the help of string interpolation

I encountered an issue in my code while attempting to utilize a variable within my TSX component. This problem arises due to the dynamic props being passed into the component, which are always a string that matches one of four keys in the "characters" obje ...

Creating an interceptor to customize default repository methods in loopback4

Whenever I attempt to access the default repository code, I need to manipulate certain values before triggering the default crud function in the repository. How can I accomplish this? For example: ... @repository.getter('PersonRepository') priva ...

Issues arise when trying to implement Angular class and it does

I'm currently facing some challenges using classes in Angular to simplify my coding process. So far, I haven't been able to get it to work properly. Below is the code snippet I'm working with and the error message that pops up: import { Wiz ...

Reactjs is throwing an error stating that the element type is invalid

When using the following code in the index page of a Next.js project: import { Typography } from '@mui/material/Typography'; import { createTheme, ThemeProvider } from '@mui/material/styles'; const theme = createTheme({ typography: ...

Disabling the use of console.log() in a live environment

In an effort to disable console logs for production environments in my angular application, I implemented the code below. While it successfully suppresses logs in Chrome, IE 11 continues to display them. Here is the snippet from main.ts: if (environment. ...