uploading files in nextjs13 using react-dropzone and possible multer

103 views Asked by At

This has been giving me a lot of headache: In my FileUploader component, I am facing this issue.

import React, { useState } from 'react';
import { Accept, useDropzone } from 'react-dropzone';
import { UploadCloud } from 'lucide-react';
import { cn } from '@/lib/utils';

interface FileUploaderProps {
    onUpload: (file: File) => void,
    acceptedFileType: Accept,
    restrictionText: string,
    buttonTextTitle: string,
}


const FileUploader = ({ 
    onUpload,
    acceptedFileType,
    restrictionText,
    buttonTextTitle
}: FileUploaderProps) => {

const [fileChange, setFileChange] = useState(false)
const [isValidFileType, setIsValidFileType] = useState(false)

const { getRootProps, getInputProps, acceptedFiles} = useDropzone({
    accept: acceptedFileType,
    maxFiles:1,
    onDrop: (acceptedFiles) => { 
        setFileChange(true)
        if(acceptedFiles.length > 0){
            setIsValidFileType(true)
        }else{
            setIsValidFileType(false)
        }
    },
    validator: fileSizeValidator
});

function bytesToMegabytes(bytes: number) {
    const megabytes = bytes / (1024 * 1024);
    return parseFloat(megabytes.toFixed(2)); // Truncate to two decimal places
}

function fileSizeValidator(file: File) {
    let fileInMB = bytesToMegabytes(file.size)
    if (fileInMB > 2048) {
      return {
        code: "size-to-long",
        message: `Your file size has exceeded the max size of 2 GB.`
      };
    }
    return null
}

const keysAsString = Object.keys(acceptedFileType).join(', ');


const files = () => {
    if(acceptedFiles.length > 0){
        const tt = acceptedFiles.map((file) => {
            return (
                <div key={file.name}>
                        <div className='flex flex-col justify-center'>
                            <p className='text-xs text-slate-500 text-center truncate-custom'>{file.name}</p>
                            <p className='text-xs text-slate-500 text-center'>{bytesToMegabytes(file.size)} MB</p>
                        </div>
                </div>
            )
        });
        return tt
    }else{
        return (
            <div className='flex flex-col justify-center'>
                <p className="text-xs text-red-500">{`Invalid file type, Only ${keysAsString} are accepted`}</p>
            </div>
        )
    }
}

const handleFile = async () => {
    const formData = new FormData();
    formData.append('file', acceptedFiles[0]);

    try {
      const response = await fetch('/api/courses/0549c28d', {
        method: 'POST',
        body: formData,
      });

      if (response.ok) {
        alert('File uploaded successfully');
      } else {
        alert('Error uploading file');
      }
    } catch (error) {
      alert(error);
    }
}

return (
      <div className="border-2 border-slate-400 border-dotted rounded-md flex flex-col items-center justify-center h-60">
          <div>
              <div {...getRootProps({ className: 'dropzone' })}>
                  <input {...getInputProps()} />
                  <div className='flex justify-center'>
                    <UploadCloud height={35} width={35} className='text-gray-500'/>
                  </div>
                  <div className={cn(
                    "flex justify-center",
                    fileChange && "my-4"
                  )}>
                    <p className='text-sky-500 font-bold cursor-pointer'>Choose files or drag and drop</p>
                  </div>
                  {fileChange ? <div>{files()}</div> : <p className='text-xs text-slate-500 text-center'>{restrictionText}</p>}
              </div>
          </div>
          <div>
              {fileChange && (
                  <div className="flex justify-center">
                      <button type="button" 
                        onClick={handleFile}
                        className={cn(
                            "rounded-lg bg-sky-500 py-2 px-4 m-4 text-white",
                            !isValidFileType && "disabled:opacity-50"
                        )}
                        disabled={!isValidFileType}
                      >
                          {buttonTextTitle}
                      </button>
                  </div>
              )}
          </div>
      </div>
    );
};

export default FileUploader;

At this level I think something is wrong with the way I am passing the data. Actually I am expecting the user to drop or select a valid valid file first. Then I show a button that says upload your iamge. Once clicked I want to upload the file now to the server and save in the "/public/uploads" directory.

Now on the server side of my nextjs app I have this:

import { NextResponse } from 'next/server'
import { IncomingForm } from 'formidable';
import fs from 'fs';
import path from 'path';
import { NextApiRequest, NextApiResponse } from 'next';

export async function POST(
    req: NextApiRequest, 
    res: NextApiResponse,
){
    try {
        const form = new IncomingForm();

        form.parse(req, async (err, fields, files) => {
          if (err) {
            console.log('Error parsing form:', err)
            return new NextResponse("Internal errror",{ status: 500 })
          }
    
          console.log(files);
          return NextResponse.json({ success: true })
          const file = files.file;
    

          if (!file) {
            return new NextResponse("No file provided",{ status: 400 })
          }
    
          // Move the uploaded file to the desired directory (/public/uploads)
          const oldPath = file.path;
          const newPath = path.join(process.cwd(), 'public/uploads', file.name);
    
          fs.renameSync(oldPath, newPath);
    
          // Handle the file, e.g., save it to disk or process it
    
          return NextResponse.json({ success: true })
        });
      } catch (error) {
        console.log("[DATA_ID]", error)
        return new NextResponse("Internal errror",{ status: 500 })
      }
}

As from here I have been trying for almost a day. I cannot succeed to file uploaded. I event try using the multer package. it seams the file is not getting at the server side successfully.

PLease I will really appreciate any assistance you can help me achiving this goal, which is to upload files in a nextjs13 app either using a free package or not.

Thanks in advance

0

There are 0 answers