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 />}
</>
);
}