Can't Put Object in S3 with NextJS
Answered
Donald Louch posted this in #help-forum
Hello,
I'm trying to create a upload function with the
I'm not sure what's wrong?!
My Tech Stack Is:
-
-
-
- Backblaze S3 (for my s3 client)
-
-
- Among Others
My Code Is:
I'm trying to create a upload function with the
@aws-sdk/client-s3
package and I can't seem to get files uploaded. When I try to upload files it just says PUT URL?x-id=PutObject net::ERR\_FAILED
andTypeError: Failed to fetch
at FetchHttpHandler.handle (fetch-http-handler.js:81:13)
at async flexibleChecksumsResponseMiddleware.js:20:20
at async throw-200-exceptions.js:10:20
at async deserializerMiddleware.js:2:26
I'm not sure what's wrong?!
My Tech Stack Is:
-
NextJS (15 Canary)
-
React/React-Dom (19 RC)
-
@aws-sdk/client-s3 (3.670.0)
- Backblaze S3 (for my s3 client)
-
@mantine/dropzone (7.13.2)
-
@mantine/core (7.13.2)
- Among Others
My Code Is:
Answered by Donald Louch
After further googling I think I have fixed it?!
I needed to add
I needed to add
"use server"
at the top of my app/actions/s3File.ts
file!4 Replies
// app/actions/s3File.ts
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"
import { revalidatePath } from "next/cache"
type S3Payload = {
uploadDestination: string
bucket: string
hostName: string
uploadEndpoint: string
}
export async function uploadFileToS3( formData: FormData, payload: S3Payload ): Promise<any[]> {
const s3 = new S3Client({
endpoint: `https://${process.env.NEXT_PUBLIC_S3_HOST_NAME}:443`,
region: process.env.NEXT_PUBLIC_S3_REGION,
credentials: {
accessKeyId: process.env.NEXT_PUBLIC_S3_ACCESS_KEY_ID!,
secretAccessKey: process.env.NEXT_PUBLIC_S3_SECRET_ACCESS_KEY!,
},
})
const { uploadDestination, bucket, hostName, uploadEndpoint } = payload
try {
const files = formData.getAll("file") as File[]
const response = await Promise.all(
files.map(async (file) => {
const {name: fileName, type: fileType, lastModified: date} = file
const fileExtension = fileType.split("/")\[1\]
const fileID = `${uploadDestination}\_CUSTOMRANDOMID` as string
const filePath = `${uploadDestination}/${fileID}.${fileExtension}`
const arrayBuffer = await file.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
const uploadFileObject = {
"acl": "public-read",
"Bucket": bucket,
"Key": filePath,
"Body": buffer,
"ContentType": fileType,
}
const uploadFile = new PutObjectCommand(uploadFileObject)
const upload = await s3.send(uploadFile)
const isFileUploaded = upload.$metadata.httpStatusCode === 200 ? true : false
console.log("Upload RES", upload)
})
)
revalidatePath("/")
return response
} catch (error) {
console.error("Error uploading file(s) to S3:", error)
throw new Error("Failed to upload file(s) to S3.")
}
}
// uploadPage/component.tsx
...
import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '@mantine/dropzone';
import { uploadFileToS3 } from "@/app/actions/s3File";
...
async function handleOnSubmit(e: any) {
setUploading(true)
const files = e
const uploadDestination = mediaType
try {
const formData = new FormData()
files.forEach((file) => formData.append("file", file))
await uploadFileToS3(formData, {
uploadDestination, // Picture | Video | Audio | ECT
bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME!,
hostName: process.env.NEXT_PUBLIC_S3_HOST_NAME!,
uploadEndpoint: `https://${process.env.NEXT\_PUBLIC\_S3\_BUCKET\_NAME!}.${process.env.NEXT\_PUBLIC\_S3\_HOST\_NAME!}`
})
} catch (error) {
console.error("File(s) couldn't be uploaded to S3", error)
}
}
return <>
...
<Dropzone
onDrop={(files) => handleOnSubmit(files)}
onReject={(files) => console.log('rejected files', files)}
loading={isUploading}
bg="none"
radius="md"
c="white"
{...props}
>
<Group justify="center" gap="2rem" style={{ pointerEvents: 'none' }} py="4rem">
<Dropzone.Accept>
<FileUploadIcon variant="twotone" />
</Dropzone.Accept>
<Dropzone.Reject>
<Cancel01Icon variant="twotone" />
</Dropzone.Reject>
<Dropzone.Idle>
<CloudUploadIcon variant="twotone" size="5rem" />
</Dropzone.Idle>
<Stack gap="0" m="0" p="0">
<Title c="white" lh="1" fw="900" ff="text" ta={{base: "center", lg: "left"}}>
{uploadTitle ? uploadTitle : "Upload Media"}
</Title>
<Text size="sm" c="grey" lh="1" ta={{base: "center", lg: "left"}}>
{helperText ? helperText : \`Please note that once you have selected your media or media's you MUST click on the "<strong>Confirm Media Upload</strong>" Button to upload your media.\`}
</Text>
</Stack>
</Group>
</Dropzone>
...
</>
I have created an issue request on the
aws-sdk-js-v3
GitHub Repo: https://github.com/aws/aws-sdk-js-v3/issues/6574After further googling I think I have fixed it?!
I needed to add
I needed to add
"use server"
at the top of my app/actions/s3File.ts
file!Answer