Server Actions with Axios
Answered
Donald Louch posted this in #help-forum
Is it possible to use Server Actions with Axios? I want to create an upload function with upload progress. I have the upload function working; just not the progress part. I did try using Axios and I'm having no luck.
Answered by Donald Louch
I have figured a possible solution? However, I'm unsure if it makes sense to do so or just seems redundant?
But I have created an API route to effectively collect the data; then send it to a controller to separate the
This does seem to be working.
The API route looks something like this:
The controller looks something like this:
I then reconfigured my server action to take in the
But I have created an API route to effectively collect the data; then send it to a controller to separate the
formData
and create different variables for the files and the payload; then I pass that controller data down to the server action.This does seem to be working.
The API route looks something like this:
// api/upload/route.ts
import { handleFileUpload } from "@/app/controllers/upload.controller";
export async function POST(req: any) {
return handleFileUpload(req)
}
The controller looks something like this:
// controllers/upload.controller.ts
import { NextResponse } from "next/server"
import { uploadFileToS3 } from "@/app/actions/s3File.ts";
export const handleFileUpload = async (req: any) => {
try {
const form = await req.formData()
const files = form.getAll("files") as any
const payloadRAW = form.get("payload")
const {
uploadDestination,
mediaID,
bucket,
uploadEndpoint,
pathname,
redirectPath
} = JSON.parse(payloadRAW)
const payload = {
uploadDestination,
mediaID,
bucket,
uploadEndpoint,
pathname,
redirectPath
} as any
const uploadURL = await uploadFileToS3(files, payload);
return NextResponse.json({ message: "success", uploadURL });
} catch (error) {
console.error("Error uploading file:", error);
return NextResponse.json({ message: "failure", reason: error.message });
}
}
I then reconfigured my server action to take in the
Files: File[]
instead of the formData: FormData
, along with some other general changes.4 Replies
Here's what I have so far:
// app/actions/s3File.ts
"use server"
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({
...
})
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, size: fileSize} = 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 = {
Bucket: bucket,
Key: filePath,
Body: buffer,
ContentType: fileType,
ContentLength: fileSize,
}
const uploadFile = new PutObjectCommand(uploadFileObject)
const upload = await s3.send(uploadFile)
})
)
revalidatePath("/")
return response
} catch (error) {
...
}
}
// 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))
// This is where I think the Axios would come in effect for upload progress?!
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">
...
</Group>
</Dropzone>
<Progress radius="0 0 0 1rem" size="xl" value={uploadProgress} color="primary" mt="0.5rem" animated />
...
</>
I have also tried to add this to the
This will upload the content through the Server Action but won't run the rest of the operation such as
async function handleOnSubmit(e: any)
functionfiles.forEach((file: string | Blob) => {
formData.append("file", file)
axios
.post(
uploadFileToS3(
formData,
s3Payload
),
{
headers: {
'x-ms-blob-type': 'BlockBlob',
},
maxContentLength: 2e10,
maxBodyLength: 2e10,
onUploadProgress: (event: any) => {
console.log("Hello from event", event)
setUploadProgress(Math.round((event.loaded / event.total) * 100))
}
}
).then((response) => {
console.log(response)
})
.catch((error) => {
if (error.response) {
console.log(error.response)
console.log("server responded")
} else if (error.request) {
console.log("network error")
} else {
console.log(error)
}
})
})
This will upload the content through the Server Action but won't run the rest of the operation such as
onUploadProgress
or the completed .then
. It returns the response error
of POST https://DEVSERVER/PAGE/[object%20Promise] 404 (Not Found)
.I have figured a possible solution? However, I'm unsure if it makes sense to do so or just seems redundant?
But I have created an API route to effectively collect the data; then send it to a controller to separate the
This does seem to be working.
The API route looks something like this:
The controller looks something like this:
I then reconfigured my server action to take in the
But I have created an API route to effectively collect the data; then send it to a controller to separate the
formData
and create different variables for the files and the payload; then I pass that controller data down to the server action.This does seem to be working.
The API route looks something like this:
// api/upload/route.ts
import { handleFileUpload } from "@/app/controllers/upload.controller";
export async function POST(req: any) {
return handleFileUpload(req)
}
The controller looks something like this:
// controllers/upload.controller.ts
import { NextResponse } from "next/server"
import { uploadFileToS3 } from "@/app/actions/s3File.ts";
export const handleFileUpload = async (req: any) => {
try {
const form = await req.formData()
const files = form.getAll("files") as any
const payloadRAW = form.get("payload")
const {
uploadDestination,
mediaID,
bucket,
uploadEndpoint,
pathname,
redirectPath
} = JSON.parse(payloadRAW)
const payload = {
uploadDestination,
mediaID,
bucket,
uploadEndpoint,
pathname,
redirectPath
} as any
const uploadURL = await uploadFileToS3(files, payload);
return NextResponse.json({ message: "success", uploadURL });
} catch (error) {
console.error("Error uploading file:", error);
return NextResponse.json({ message: "failure", reason: error.message });
}
}
I then reconfigured my server action to take in the
Files: File[]
instead of the formData: FormData
, along with some other general changes.Answer