Giter Club home page Giter Club logo

Comments (20)

dong-qian avatar dong-qian commented on July 25, 2024 11

@Phantomat0 Hi, I already slove the issue. Here is an example

import { supabase } from "lib/supabase";
import formidable from "formidable";
import fs from "fs";

async function handler(req, res) {
  const form = new formidable.IncomingForm();
  const uploadFile = async () => {
    // eslint-disable-next-line
    return new Promise((resolve, reject) => {
      form.parse(req, async function (err, fields, files) {
        let filepath = `${fields.id}/${files.file.originalFilename}`;
        filepath = filepath.replace(/\s/g, "-"); // IN CASE YOU NEED TO REPLACE SPACE OF THE IMAGE NAME
        const rawData = fs.readFileSync(files.file.filepath);
        const { error } = await supabase.storage
          .from("property-images")
          .upload(filepath, rawData, {
            contentType: files.file.mimetype,
          });

        if (error || err) {
          return reject({ success: false });
        }
        /** YOU DO NOT NEED BELOW UNLESS YOU WANT TO SAVE PUBLIC URL OF THE IMAGE TO THE DATABASE
        await supabase.from("property-image").insert({
          url: `${process.env.NEXT_PUBLIC_SUPABASE_STORAGE_BASE_URL}/property-images/${filepath}`,
          property_id: fields.id,
        });
       */
        resolve({ success: true });
      });
    });
  };

  try {
    await uploadFile();
    res.status(200).send({ success: true });
  } catch (err) {
    res.status(400).send({ success: false });
  }
}

export const config = {
  api: {
    bodyParser: false,
  },
};

export default handler;

from storage.

brikiernan avatar brikiernan commented on July 25, 2024 1

@qiandongyq Thanks so much for posting your solution!!

This was the key line for me:
const rawData = fs.readFileSync(files.file.filepath);

I was using the filepath as a string and not as a buffer. Using it as a string was actually working on Cloudinary but not supabase storage which is interesting. So it was just adding to supabase storage as the actual filepath and not the actual file.

Thanks again!

from storage.

Phantomat0 avatar Phantomat0 commented on July 25, 2024 1

Use the File type from formidable, not from Node. You can tell you're using the Node File type since "name" doesn't exist on formidable Files, but rather "originalFilename". This implies you're using the @types package from formidable btw.

Import the types as so:

import { Fields, File, Files } from "formidable";

from storage.

thebengeu avatar thebengeu commented on July 25, 2024

Thanks for the detailed bug report @juanzgc, we'll look into it.

from storage.

juanzgc avatar juanzgc commented on July 25, 2024

I was able to get it to work from the Client Side (instead of Server side) - so this might just be an issue on my end.

However, a bug that I do believe may be an issue for other people is the following:

Attempt to upload an image with an unusual path.

      const {data, error} = await supabase.storage.from('your-bucket')
        .upload('folder/folder2//var/folders/file', file, {
          cacheControl: 3600,
          upsert: false,
          contentType: "image/jpeg"
        })

Now attempt to delete folder and you'll notice you won't be able to.

The issue comes from the double // in the path.

image

The smaller folders (no names), were created and now my main folder bike can no longer be deleted or renamed.

Thanks again for looking into this @thebengeu

from storage.

thebengeu avatar thebengeu commented on July 25, 2024

@juanzgc thanks for the other bug report!

@inian is there any way we can/should handle // in paths? Should we just add validation to reject such paths?

from storage.

juanzgc avatar juanzgc commented on July 25, 2024

In my opinion, we should add validation to reject paths with empty folders. Also we should allow empty folders in general.

If you create a new folder, save it, and then rename the folder it allows for empty folder names. This should be rejected.

An issue I have now, is that I'm no longer able to delete this folder. In my case it would be very helpful to investigate how to remove this bug so I can delete this faulty folder.

from storage.

inian avatar inian commented on July 25, 2024

Should we just add validation to reject such paths?

Yup this is a good idea, I remember debugging another issue where the folder name was undefined in s3 but the files were not actually reachable from our api. This was caused due to the double slash IIRC

from storage.

dong-qian avatar dong-qian commented on July 25, 2024

any updates on this issue? I have the same issue that the file is only 15 bytes use formidable in nextjs

from storage.

thebengeu avatar thebengeu commented on July 25, 2024

@qiandongyq would you be able to provide a sample of your code please?

from storage.

Phantomat0 avatar Phantomat0 commented on July 25, 2024

Same issue here, every file is 15 bytes and following the URL leads to an object.

from storage.

Phantomat0 avatar Phantomat0 commented on July 25, 2024

@qiandongyq Wow man, you are a life saver! I've been dealing with this problem the entire day. Worked like a charm, except I had to rename a few things like instead of "originalFilename" to just "name" and "filepath" to "path". It's all working now, thanks!

from storage.

oarsoy avatar oarsoy commented on July 25, 2024

@Phantomat0 Hi, I already slove the issue. Here is an example

import { supabase } from "lib/supabase";
import formidable from "formidable";
import fs from "fs";

async function handler(req, res) {
  const form = new formidable.IncomingForm();
  const uploadFile = async () => {
    // eslint-disable-next-line
    return new Promise((resolve, reject) => {
      form.parse(req, async function (err, fields, files) {
        let filepath = `${fields.id}/${files.file.originalFilename}`;
        filepath = filepath.replace(/\s/g, "-"); // IN CASE YOU NEED TO REPLACE SPACE OF THE IMAGE NAME
        const rawData = fs.readFileSync(files.file.filepath);
        const { error } = await supabase.storage
          .from("property-images")
          .upload(filepath, rawData, {
            contentType: files.file.mimetype,
          });

        if (error || err) {
          return reject({ success: false });
        }
        /** YOU DO NOT NEED BELOW UNLESS YOU WANT TO SAVE PUBLIC URL OF THE IMAGE TO THE DATABASE
        await supabase.from("property-image").insert({
          url: `${process.env.NEXT_PUBLIC_SUPABASE_STORAGE_BASE_URL}/property-images/${filepath}`,
          property_id: fields.id,
        });
       */
        resolve({ success: true });
      });
    });
  };

  try {
    await uploadFile();
    res.status(200).send({ success: true });
  } catch (err) {
    res.status(400).send({ success: false });
  }
}

export const config = {
  api: {
    bodyParser: false,
  },
};

export default handler;

I still cannot figure it out. Is it possible to share the client side as well?

from storage.

Phantomat0 avatar Phantomat0 commented on July 25, 2024

@oarsoy I actually did not implement this client side yet, just testing using postman so I could set up the API route, but it worked there. Im guessing it would be something like

    const formData = new FormData();
    formData.append(
      "file",
      new File([recording], "filename", {
        type: "application/octet-stream",
      })
    );

    // Send to our server
    fetch(API_LINK, {
      method: "POST",
      body: formData,
    });

Also important note in the example above, you need to name "files.file" to whatever key you are using in your form data. For example if you did:

    formData.append(
      "imageFile",
      new File([recording], "filename", {
        type: "application/octet-stream",
      })
    );

instead of doing "files.file" you would do "files.imageFile" since the "files" object is an object of your formData, so you need to specify which file you want to upload, in this case "imageFile".

from storage.

dong-qian avatar dong-qian commented on July 25, 2024

@Phantomat0
My client use react-dropzone, but you can use native HTML file upload, in the end, you will send the file to the server API

EX:
FileUploader component.

import { useCallback } from "react";
import { useDropzone } from "react-dropzone";
import cx from "clsx";

export function FileUploader({ uploadHandler, fileType, isUploading }) {
  const onDrop = useCallback(
    async (acceptedFiles) => {
      uploadHandler(acceptedFiles[0]);
    },
    [uploadHandler]
  );

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    maxFiles: 1,
    accept: `${fileType}/*`, // for image only, you can put image/*
    disabled: isUploading,
  });

  return (
    <div
      {...getRootProps({
        className: cx("w-full h-24 tw-center cursor-pointer px-2"),
      })}
    >
      <input {...getInputProps()} />
      {isUploading ? (
        <span className="animate-pulse text-lg text-pending">
          Processing...
        </span>
      ) : isDragActive ? (
        <p>{`Drop the ${fileType} here ...`}</p>
      ) : (
        <p>{`Drag 'n' drop ${fileType} here, or click to select ${fileType}`}</p>
      )}
    </div>
  );
}

Submit handler

  const uploadHandler = async (file) => {
    setIsUploading(true);
    const form = new FormData();
    form.append("file", file); // this is name of the image file you will receive on server
    form.append("id", id);
    await fetch("/api/storage/upload-image", {
      method: "POST",
      body: form,
    });
    propertyImagesMutate(); // you don't need this, this is for swr
    setIsUploading(false);
  };

from storage.

dong-qian avatar dong-qian commented on July 25, 2024

@Phantomat0 sorry, I'm replying to the wrong person.
@oarsoy Please see my comment above

from storage.

roker15 avatar roker15 commented on July 25, 2024
originalFilename

What with typescript?
originalFilename property does not exist, it shows

from storage.

Phantomat0 avatar Phantomat0 commented on July 25, 2024

Just add the types.

      form.parse(req, async function (err: any, _fields: Fields, files: Files) {
        const file = files.file as File
        const filepath = file.originalFilename?.replace(/\s/g, "-") as string
        const rawData = fs.readFileSync(file.filepath);
        const { error } = await supabase.storage

from storage.

roker15 avatar roker15 commented on July 25, 2024
const file = files.file as File

you can not do this, as formidable.File can not be cast as File. I have tried.

Here is the lint

Conversion of type 'File | File[]' to type 'File' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'File[]' is missing the following properties from type 'File': lastModified, name, webkitRelativePath, size, and 4 more

from storage.

bnjmnt4n avatar bnjmnt4n commented on July 25, 2024

Closing this, since there doesn't appear to be an issue on the storage API side, and the original error was due to uploading the filepath instead of the actual file contents when using formidable.

from storage.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.