文件上传到GCS使用签名URL从Nextjs -错误代码403

sxissh06  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(198)

目的是- ->在api/fileupload.js文件中生成签名的url,以便将文件上传到GCS。->通过nextjsAPI从Nextjs服务器获取签名API - localhost://3000/api/fileupload ->使用index.jsx文件中生成的签名URL将文件上传到gcs
已成功生成签名URL。但是当将图像主体作为表单数据上传到GCS时,发生403代码错误。这是响应体。

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

在index.jsx文件中上传文件作为表单数据的方法是否正确?还是我错过了什么
两个文件如下所示-
nextjs文件的index.jsx-

import { useState } from "react";

    export default function Home() {
    const [url, setUrl] = useState("");
    const [file, setFile] = useState<any>(null);
    const [dataloaded, setDataloaded] = useState(true);
  
    const handleSubmit = async (e: any) => {
    setDataloaded(false);

    e.preventDefault();

    let formData = new FormData();
    formData.append("file", file.data);
    formData.append("Content-Type", `${file.data.type}`);

    console.log(file);
    const response = await fetch("http://localhost:3000/api/fileupload", {
      method: "POST",
      body: formData
    });
    const responseWithBody = await response.json();
    console.log(responseWithBody);
    setDataloaded(true);
    if (response.status === 200) {
      setUrl(responseWithBody.url);
    } else {
      console.log("error in generating url");
    }
    const response1 = await fetch(
      responseWithBody.url,

      {
        mode: "no-cors",
        method: "POST",
        body: formData,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "content-type": "image/png"
        }
      }
    );
    console.log(response1);
  };
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 ? (
              <img src={file.preview} alt="File to upload" />
            ) : (
              <img
            src="https://raw.githubusercontent.com/koehlersimon/fallback/master/Resources/Public/Images/placeholder.jpg"
             alt="Fallback"
           />
         )}
       </div>
       <div className="file-name">
         {file && file.data.name}
         {url && (
           <a href={url} target="_blank" rel="noreferrer">
             FiExternalLink
           </a>
         )}
       </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>
     </form>
   </div>
 </>
 );
 }

fileupload.js在API/文件夹-

import { Storage } from "@google-cloud/storage";
import multer from "multer";
import type { NextApiRequest, NextApiResponse } from "next";

const storage = new Storage({
  keyFilename: `service_account_key.json`,
  projectId: "my-project-id"
});
const bucketName = "my-bucket-name";

async function parseFormData(
  req: NextApiRequest & { files?: any },
  res: NextApiResponse
) {
  const storage = multer.memoryStorage();
  const multerUpload = multer({ storage });
  const multerFiles = multerUpload.any();
  await new Promise((resolve, reject) => {
    multerFiles(req as any, res as any, (result: any) => {
      if (result instanceof Error) {
        return reject(result);
      }
      return resolve(result);
    });
  });
  return {
    fields: req.body,
    files: req.files
  };
}

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 result = await parseFormData(req, res);
  // console.log(result);
  const file = storage
    .bucket(bucketName)
    .file(result?.files[0]?.originalname || "new-file.png");
  const [url]: any = await file.getSignedUrl(options);

  console.log("Generated PUT signed URL:");
  console.log(url);

  res.status(200).json({ url: url });
}
wwwo4jvm

wwwo4jvm1#

我想发送原始文件的名称,在该文件上将生成一个签名的URL,并使用Nextjs GET API接收该签名的URL。
下面是解决方案代码-在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 });
}

通过Nextjs GET API获得签名的URL,并使用签名的URL、从事件的目标对象保存的数据以及标头中的实际内容类型调用PUT API。
index.jsx文件-

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

相关问题