#Server Actions with Axios

6 messages · Page 1 of 1 (latest)

fickle patio
#

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.

vernal apexBOT
#

🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord

🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize

✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)

fickle patio
#

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 async function handleOnSubmit(e: any) function

files.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).

fickle patio
#

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 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.