cloudinary_npm icon indicating copy to clipboard operation
cloudinary_npm copied to clipboard

Uploading file as buffer does not works in production.

Open nirvikpurkait opened this issue 1 year ago • 12 comments

Describe the bug in a sentence or two.

When uploading a file as an byteArrayBuffer in Next.Js application , if the server is in development, the file gets uploaded, but if the server is in production, it throws error. (Error screenshot is provided below)

Issue Type (Can be multiple)

  • Production (works in development, but not in production)

Steps to reproduce

  • Go to this link.
  • Enter CLOUDINARY_API_COULD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET in a .env file.
  • Run npm run dev , it works (file uploads).
  • Build the application, run npm run build, then launch application, run npm start.
  • Try to to upload a file, (throws error).

Error screenshots

Screenshot 2024-02-01 003806

Versions and Libraries (fill in the version numbers)

Cloudinary_NPM SDK version Node - v20.9.0 (codesandbox), v20.9.0 (system) NPM - 9.8.1(codesandbox), 10.2.4 (system)

Config Files (Please paste the following files if possible)

{
  "name": "nextjs",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "cloudinary": "^2.0.0",
    "next": "14.1.0",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.4.17",
    "eslint": "^8",
    "eslint-config-next": "14.1.0",
    "postcss": "^8",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

Repository

codesandbox

nirvikpurkait avatar Jan 31 '24 19:01 nirvikpurkait

Hi @nirvikpurkait ,

Thanks for contacting Cloudinary.

The codesandbox you provided works fine without a problem. I looked at the logs of your account and I see that there could be a problem with the API Config in Production. Can you please check the settings accordingly of the API Key and API Secret?

Thanks, Sree

skalahasti-cloudinary avatar Feb 01 '24 12:02 skalahasti-cloudinary

Hi @skalahasti-cloudinary , thanks for your response.

As you said earlier -

I looked at the logs of your account and I see that there could be a problem with the API Config in Production.

Maybe you looked at the logs, when I accidentally re-run the code, after I changed the .env file, before sharing the codesandbox.

Anyway, I have checked the log also, built and run the app again. It still gives me the error.

I made a video about the error in production, and I wanted to attach the video here, but the video is too large to upload here, so I am providing a link for the video.

In case you can't find the video or don't have time to see through it, (as it is more than 2 min.) I am also going to provide some snapshot of it.

  • Works fine with dev server 1

  • build the app with same .env 2

  • Error in production 3

I reproduced the error with same .env file using in development and production (without changing any value), after the video I changed the .env file again (trying not to share the API SECRET).

nirvikpurkait avatar Feb 01 '24 16:02 nirvikpurkait

@nirvikpurkait thx for the reply.

So what we're seeing in the failed uploads is that the API key param contains SVG file data as well instead of it being part of the file parameter e.g.

5xxxxxxxxxxxxx8
--xxxxxxxxxxx
Content-Disposition: form-data; name="file"; filename="file"
Content-Type: application/octet-stream

<svg width="638" height="599" viewBox="0 xxxxxx 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="text">
<path id="The" d="M2xxxxxx36.0...

As you can see, you need to only send 5xxxxxxxxxxxxx8.

Please check why in prod mode your app is including the SVG file data in the API key instead of the file parameter.

tommyg-cld avatar Feb 02 '24 14:02 tommyg-cld

Hello, I'm facing the exact same issue. Everything works perfectly in development mode, but in production, I encounter the same error.

When I submit a form with files using a Server Action, I encounter an error when trying to upload.

Here's the relevant code:

const folderPrefix = getCloudinaryEnv();
const folder = 'test';
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);

return await new Promise((resolve, reject) => {
  cloudinary.uploader
    .upload_stream(
      { tags, folder: `${folderPrefix}/${folder}` },
      (error, result) => {
        if (error) {
          reject(error);
          return;
        }
        resolve(result);
      },
    )
    .end(buffer);
});

The error :

error {
  message: `Server return invalid JSON response. Status Code 500. SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON`,
  name: 'Error',
  http_code: 500
}

@nirvikpurkait Did you find a solution?


Framework: Next.js 14.1.0 Hosting: Vercel

thomaslenaour avatar Feb 11 '24 17:02 thomaslenaour

I just found a potential solution (or instead an alternative) :

I convert files into Base 64 before uploading, and I use the cloudinary.uploader.upload() method.

Here's my code :

import mime from 'mime';

const file = formData.get('image') as File;
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);
const base64File = Buffer.from(buffer).toString('base64');
const fileMimeType = mime.getType(file.name);

await cloudinary.uploader.upload(
  `data:${fileMimeType};base64,${base64File}`,
  { tags: ['avatars'], folder: 'images' },
);

Don't forget to do additional checks before uploading (file size, type, etc.).

This code is working for me in production and when I build my project.

Feel free to give me your feedback!


Framework: Next.js 14.1.0 Hosting: Vercel

thomaslenaour avatar Feb 12 '24 06:02 thomaslenaour

@thomaslenaour is it happening when uploading SVG files or any file types?

tommyg-cld avatar Feb 12 '24 16:02 tommyg-cld

@tommyg-cld In my case it was JPG files. I think this is the same problem for any types, I didn't test other files.

thomaslenaour avatar Feb 12 '24 16:02 thomaslenaour

@thomaslenaour are you able to share a your project via codesandbox or similar so that we can replicate?

tommyg-cld avatar Feb 12 '24 16:02 tommyg-cld

@thomaslenaour is it happening when uploading SVG files or any file types?

@tommyg-cld As of now it is happening with all jpg, png, svg file type.

import mime from 'mime';

const file = formData.get('image') as File; const arrayBuffer = await file.arrayBuffer(); const buffer = new Uint8Array(arrayBuffer); const base64File = Buffer.from(buffer).toString('base64'); const fileMimeType = mime.getType(file.name);

await cloudinary.uploader.upload( `data:${fileMimeType};base64,${base64File}` { tags: ['avatars'], folder: 'images' }, );

@thomaslenaour Thanks for sharing the idea, what ever my need is, it can be fulfilled by that.

  • But what if someone needs to upload a stream as written in documentation . In that case it is throwing an error.

nirvikpurkait avatar Feb 14 '24 12:02 nirvikpurkait

@nirvikpurkait are you able to share your codesandbox project where we can replicate this?

tommyg-cld avatar Feb 14 '24 12:02 tommyg-cld

@nirvikpurkait are you able to share your codesandbox project where we can replicate this?

codesandbox

Here you can put your api-key, secret, cloud name, and try it out.

I have also provided some screenshot in this comment

nirvikpurkait avatar Feb 14 '24 12:02 nirvikpurkait

@nirvikpurkait thanks, i was able to replicate, please leave the sandbox running if you don't mind as we have an internal bug for this and the codesandbox link is really handy.

we will keep you posted.

tommyg-cld avatar Feb 16 '24 17:02 tommyg-cld

I just found a potential solution (or instead an alternative) :

I convert files into Base 64 before uploading, and I use the cloudinary.uploader.upload() method.

Here's my code :

import mime from 'mime';

const file = formData.get('image') as File;
const arrayBuffer = await file.arrayBuffer();
const buffer = new Uint8Array(arrayBuffer);
const base64File = Buffer.from(buffer).toString('base64');
const fileMimeType = mime.getType(file.name);

await cloudinary.uploader.upload(
  `data:${fileMimeType};base64,${base64File}`,
  { tags: ['avatars'], folder: 'images' },
);

Don't forget to do additional checks before uploading (file size, type, etc.).

This code is working for me in production and when I build my project.

Feel free to give me your feedback!

Framework: Next.js 14.1.0 Hosting: Vercel

I want to directly add base64 file to cloudinary any idea about it ?

Ja3mamtora avatar Feb 27 '24 17:02 Ja3mamtora

@Ja3mamtora can you provide more details on what you're trying to do? The post you are referring does exactly that, are you having issues using it? Note this was a temp workaround suggested by @thomaslenaour until we fix the issue on our end.

tommyg-cld avatar Feb 28 '24 14:02 tommyg-cld

Hello, I can confirm that the upload_stream() function works great for me in production with the last release v.2.0.3.

Thanks Cloudinary team for the fix!

thomaslenaour avatar Mar 08 '24 06:03 thomaslenaour

@thomaslenaour that is great to hear, thanks for the update. I will be closing this issue then.

tommyg-cld avatar Mar 08 '24 08:03 tommyg-cld

Thanks for the fix. Now it is working fine with all jpg, png, svg for me.

nirvikpurkait avatar Mar 08 '24 11:03 nirvikpurkait