Next.js Discord

Discord Forum

Passing file to server action for upload?

Answered
American Shorthair posted this in #help-forum
Open in Discord
Avatar
American ShorthairOP
I'm having an issue figuring out if it's possible to pass an image to a server action for upload to database. If possible, id like to do this server side to avoid loading the firebase packages client side but I keep getting this error

Application error: a client-side exception has occurred (see the browser console for more information).


react-dom.development.js:7749 Uncaught Error: Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.
    at Array.resolveToJSON (react-server-dom-webpack-client.browser.development.js:732:31)


Here is my server action attempt
'use server';

import { storage } from './firebase';
import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';

export async function uploadFileToFirebase(file, path) {
  const storageRef = ref(storage, `${path}`);

  try {
    const snapshot = await uploadBytes(storageRef, file);
    const downloadURL = await getDownloadURL(snapshot.ref);
    console.log('File uploaded!', downloadURL);

    return downloadURL;
  } catch (error) {
    console.error('Error uploading file:', error);
    throw error; 
  }
}


handle drop from mui file drop component
  const handleDrop = useCallback(
    async (acceptedFiles) => {
      const file = acceptedFiles[0];
      if (!file) return;

      try {
        const downloadURL = await uploadFileToFirebase(
          file,
          `blog/${currentPost.urlTitle}/cover-image`
        );

        const newFile = {
          ...file,
          preview: downloadURL,
        };
      } catch (error) {
       
      }
    },
    [setValue, currentPost.urlTitle]
  );
Answered by American Shorthair
So this is what I ended up doing. it works but I would not assume it's a good or secure way to do it i'm far from good at this stuff. I do beleive server action file sending has a size limitation. Not sure what it is though.


This is my handleDrop for my mui image drop handling.
  const handleDropMulti = useCallback(
    (acceptedFiles) => {
      setUploadingImages(true); // Indicate uploading

      const uploadPromises = acceptedFiles.map((file) => {
        const formData = new FormData();
        formData.append('file', file);
        formData.append('urlTitle', currentPost.urlTitle);
        return firebaseUploadBlogMultiImage(formData);
      });

      Promise.all(uploadPromises)
        .then((imageDataArray) => {
          setImages(imageDataArray); // Directly set state with image data from server action
          setUploadingImages(false);
        })
        .catch((error) => {
          console.error('Error uploading files:', error);
        });
    },
    [currentPost.urlTitle, setImages, setUploadingImages]
  );



And then here is my server action handling the file upload
export async function firebaseUploadBlogMultiImage(formData) {
  const file = formData.get('file');
  const urlTitle = formData.get('urlTitle');

  const path = `${urlTitle}/blog-content/${file.name}`;
  const storageRef = ref(storage, path);

  try {
    const snapshot = await uploadBytes(storageRef, file);
    const downloadURL = await getDownloadURL(snapshot.ref);

    const imageData = {
      name: file.name,
      size: file.size,
      preview: downloadURL, // Add this line
      // ...other properties if needed
    };

    return imageData;
  } catch (error) {
    console.error('Error uploading image:', error);
    throw error; // Rethrow the error for caller to handle
  }
}
View full answer

4 Replies

Avatar
American ShorthairOP
So I was able to finally get it to work by passing the file in with all the form data then accessing the file that way. Not sure if there is a better way, but this is the only way i could find to get it work. still open to hearing a better way if anybody has it. Thank you
Avatar
Eastern Phoebe
@American Shorthair Hi JinMa, im currently facing the same issue could you please share your final code with me?
Avatar
American ShorthairOP
So this is what I ended up doing. it works but I would not assume it's a good or secure way to do it i'm far from good at this stuff. I do beleive server action file sending has a size limitation. Not sure what it is though.


This is my handleDrop for my mui image drop handling.
  const handleDropMulti = useCallback(
    (acceptedFiles) => {
      setUploadingImages(true); // Indicate uploading

      const uploadPromises = acceptedFiles.map((file) => {
        const formData = new FormData();
        formData.append('file', file);
        formData.append('urlTitle', currentPost.urlTitle);
        return firebaseUploadBlogMultiImage(formData);
      });

      Promise.all(uploadPromises)
        .then((imageDataArray) => {
          setImages(imageDataArray); // Directly set state with image data from server action
          setUploadingImages(false);
        })
        .catch((error) => {
          console.error('Error uploading files:', error);
        });
    },
    [currentPost.urlTitle, setImages, setUploadingImages]
  );



And then here is my server action handling the file upload
export async function firebaseUploadBlogMultiImage(formData) {
  const file = formData.get('file');
  const urlTitle = formData.get('urlTitle');

  const path = `${urlTitle}/blog-content/${file.name}`;
  const storageRef = ref(storage, path);

  try {
    const snapshot = await uploadBytes(storageRef, file);
    const downloadURL = await getDownloadURL(snapshot.ref);

    const imageData = {
      name: file.name,
      size: file.size,
      preview: downloadURL, // Add this line
      // ...other properties if needed
    };

    return imageData;
  } catch (error) {
    console.error('Error uploading image:', error);
    throw error; // Rethrow the error for caller to handle
  }
}
Answer
Avatar
American ShorthairOP
Oh forgot to tag