我正在创建一个管理仪表板,您可以在其中对产品进行 CRUD 操作。当我上传图像时,它被上传到AWS存储桶,但它没有显示在前端,它显示禁止访问,但我已向用户授予所有权限。在第二次上传图像时,第一个图像可见,这是我的主要问题。保存产品详细信息后,所有图像都会添加到 Mongo 数据库中。
这是代码
ProductForm.js
"use client"
// import axios from 'axios';
import { useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react'
const ProductForm = ({
_id,
title: existingTitle,
description: existingDescription,
price: existingPrice,
images: existingImages
}) => {
const router = useRouter();
// console.log(existingTitle, existingDescription, existingPrice, _id);
const [title, setTitle] = useState(existingTitle || "");
const [description, setDescription] = useState(existingDescription || "");
const [price, setPrice] = useState(existingPrice || "");
const [images, setImages] = useState(existingImages || []);
// To load the existing product details on opening the page / update state for form fields when props change
useEffect(() => {
setTitle(existingTitle || "");
setDescription(existingDescription || "");
setPrice(existingPrice || "");
setImages(existingImages || []);
}, [existingTitle, existingDescription, existingPrice, existingImages])
// Form submission
const saveProduct = async (e) => {
e.preventDefault();
const productData = { title, description, price, _id, images };
console.log("productData", productData);
// Watch the video to send data using AXIOS
// UPDATE the product if ID exits
if (_id) {
await fetch("/api/products", {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(productData),
})
}
// POST (CREATE) the data if ID does not exist
else {
await fetch("/api/products", {
method: 'POST',
headers: {
'Content-Type': 'multipart',
},
body: JSON.stringify(productData),
})
}
// setgotoProducts(true); // might not need
router.push('/products'); // redirect to the products page
}
const [start, setstart] = useState(true)
useEffect(() => {
console.log("Useffect: ", images);
}, [start])
useEffect(() => {
console.log('Useffect Updated images:', images);
}, [images]);
const uploadImages = async (e) => {
const files = e.target?.files; // e.target?.files
// console.log(files);
if (!files || files.length == 0) {
console.error('No files selected');
return;
}
const data = new FormData();
// files are stored in a FileList (array like) object, convert it into an Array to use forEach
for (const file of files) {
data.append('files', file)
// console.log(file);
}
// or
// Array.from(files).forEach(file => data.append('files', file)) // Add each file to FormData
// Print Formdata object
for (let pair of data.entries()) { // let [key,value] of data.entries()
// console.log(pair);
}
const res = await fetch('/api/upload', {
method: 'POST', // headers are automatically added using FormData
body: data,
})
.then(res2 => res2.json())
.then(linksData => {
setImages(prevImages => [...prevImages, ...linksData.links]);
// console.log('Uploaded images:', ...linksData.links);
})
console.log('After upload:', images); // not getting updated
// const res = await fetch('/api/upload', {
// method: 'POST',
// body: data,
// });
// const linksData = await res.json();
// const updatedImages = [...images, ...linksData.links]; // Create a new array
// setImages(updatedImages); // Update state
// console.log('Updated images:', updatedImages); // Use the local variable to debug
}
return (
<form onSubmit={e => { saveProduct(e) }} className='max-w-[600px] ml-10'>
<div className='flex flex-col gap-1'>
<label>Name</label>
<input
type="text"
value={title}
onChange={e => setTitle(e.target.value)} />
</div>
<div className='flex flex-col gap-1'>
<label>Description</label>
<textarea name="description" id="" value={description} onChange={e => setDescription(e.target.value)} rows={5} />
</div>
<div className='flex flex-col gap-1'>
<label>Price (in CAD)</label>
<input name='price' type="text" value={price} onChange={e => setPrice(e.target.value)} />
</div>
<div className='flex flex-col gap-1'>
<label>Photos</label>
<div className="flex flex-wrap gap-3">
{!!images.length && images.map((link) => (
<div key={link} className="w-28 h-28">
<img className="rounded-lg w-full h-full object-fill" src={`${link}?t=${Date.now()}`} alt="productImage" />
</div>
))}
{/* */}
<label className='cursor-pointer w-28 h-24 flex gap-1 items-center rounded-lg bg-gray-200 text-gray-500'>
<img src="/upload.svg" alt="" />Upload
<input type="file" onChange={e => uploadImages(e)} className='hidden' name="" id="" multiple />
</label>
</div>
</div>
<button type='submit' className='btn-primary mt-5'>Save</button>
</form>
)
}
export default ProductForm
api/upload/route.js
import { NextResponse } from 'next/server';
// import { writeFile } from 'fs/promises'; // to save to localStorage
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import fs from 'fs';
const bucketName = "namjot-next-ecommerce";
export async function POST(req) {
// Get the files from the request
const data = await req.formData()
const files = data.getAll('files')
// console.log('data', data);
// console.log(files);
if (!files) return NextResponse.json({ success: false });
// const bytes = await files.arrayBuffer();
// const buffer = Buffer.from(bytes)
// const filepath = `${files.name}` //public/uploads/
// await writeFile(filepath, buffer)
// console.log(filepath);
// AWS S3 Bucket
const client = new S3Client({
region: 'us-east-1',
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
});
const links = [];
// Upload each file to AWS
for (const file of files) {
// Unique file name to avoid duplicate images in Bucket
const ext = file.name.split('.').pop() // pop() removes the last element from an array and returns that element
const newFilename = Date.now() + "." + ext;
// console.log({ ext, file });
// console.log(newFilename);
// Convert file to Buffer
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes)
// Upload to S3 bucket
client.send(new PutObjectCommand({
Bucket: bucketName,
Key: newFilename,
Body: buffer,
ACL: 'public-read',
ContentType: file.type,
}))
const link = `https://${bucketName}.s3.us-east-1.amazonaws.com/${newFilename}`;
links.push(link);
}
return NextResponse.json({ links });
}
// disable the default bodyparser that parses the FormData
export const config = {
api: {
bodyParser: false,
},
};
我认为您需要在 AWS s3 存储桶上应用读写策略。 https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_rw-bucket.html
您能否尝试重新加载页面以查看您的最新图像是否仍在显示?有时浏览器需要时间来加载它。
但这与对象读取策略完全相关。您可能在 AWS S3 上应用策略规则时遗漏了一些内容。