0

having issues uploading file from NodeJs server side, found 100 posts and reasearches but nothing works, would appreciate any help.

Structure of the App

  1. Front App - React Admin framework receving file and i encode in base64 the content of the image to send to API

  2. Backend - NestJS App - receving base64 image in API

  3. From my backend API need to send file to an external backend (Python API) to upload - here is the problem

Please see below my code, something wrong with the file from JS

i have tried several methods and all of them ends in same error

1 solution

  • converting base64 image in buffer and send to external backend to upload the file
  • have tried to pass as well cleanImageBuffer but no changes
import axios from 'axios';
import FormData from 'form-data';

export async function upload(
  fileBase64: string,
  filename: string
): Promise<any> {

  const buffer = Buffer.from(fileBase64, 'base64')
  const extension = fileBase64.substring(fileBase64.indexOf('/') + 1, fileBase64.indexOf(";base64"))
  const cleanBase64 = fileBase64.replace(/^data:image\/png;base64,/, '')
  const cleanImageBuffer = Buffer.from(cleanBase64, 'base64')

  const formData = new FormData();
  // have tried to pass as well cleanImageBuffer but no changes
  formData.append('file', buffer);
  formData.append('fileName', filename + '.' + extension);
  formData.append('namespace', 'test');
  
  return await axios
    .post('external_api_url', JSON.stringify(formData), {
      headers: {
        Authorization: `Bearer token`,
        ContentType: 'multipart/form-data'
      }
    })
    .then((response) => {
      console.log('response = ' + JSON.stringify(response))
    })

result 1 solution

{
    "status": "error",
    "error": {
        "code": "bad_request",
        "message": "file Expected UploadFile, received: <class 'str'>"
    }
}

2 solution

  • from base64 image received saving on my disk
  • after creating a stream and sending the image
export async function upload (
  fileBase64: string,
  filename: string
): Promise<any> {

  const extension = fileBase64.substring(fileBase64.indexOf('/') + 1, fileBase64.indexOf(";base64"))
  const cleanBase64 = fileBase64.replace(/^data:image\/png;base64,/, '')

  const TMP_UPLOAD_PATH = '/tmp'

  if (!fs.existsSync(TMP_UPLOAD_PATH)) {
    fs.mkdirSync(TMP_UPLOAD_PATH);
  }

  fs.writeFile(TMP_UPLOAD_PATH + '/' + filename + '.' + extension, cleanBase64, 'base64', function(err) {
    console.log(err);
  })

  const fileStream = fs.createReadStream(TMP_UPLOAD_PATH + '/' + filename + '.' + extension)

  const formData = new FormData();
  formData.append('file', fileStream, filename + '.' + extension);
  formData.append('fileName', filename + '.' + extension);
  formData.append('namespace', 'test');

  return await axios
    .post('external_api_url', formData, {
      headers: {
        Authorization: `Bearer token`,
        ContentType: 'multipart/form-data'
      }
    })
    .then((response) => {
      console.log('response = ' + JSON.stringify(response))
    })
}

result 2 solution

{
    "status": "error",
    "error": {
        "code": "bad_request",
        "message": "file Expected UploadFile, received: <class 'str'>"
    }
}

other solution that ended in same result

  • tried to use fetch from node-fetch - same result
  • found out that some people had an outdated version of axios and having this issues, i have installed latest axios version 1.1.3 but same result

best scenario that i need

  • from base64 image received
  • convert in buffer and send file to external Python API so to avoid saving the file on local disk

would appreciate any help

below is a python example that works but not JS (JS nothing works)

import requests

url = "http://127.0.0.1:8000/external_api"

payload={'namespace': 'test'}
files=[
  ('file',('lbl-pic.png',open('/local/path/lbl-pic.png','rb'),'image/png'))
]
headers = {
  'Authorization': 'Bearer token'
}

response = requests.request("POST", url, headers=headers, data=payload, files=files)

print(response.text)
aaa
  • 446
  • 2
  • 8
  • 29

2 Answers2

1

Just a suggestion:

  1. First see if you can make it work with regular HTML file input (don't complicate with Base64 yet), as described here https://stackoverflow.com/a/70824288/2347084
  2. If (1) works, then try converting base64 into a File object as suggested here https://stackoverflow.com/a/47497249/2347084
  3. Combine (2) and (1)
yagger
  • 2,975
  • 1
  • 16
  • 21
  • is weird node-fetch, end up only in type error like node_fetch_1.File is not a function, node_fetch_1,File is not a constructor, on server with node-fetch can not solve the error, anything used from node-fetch ends in type error, to fetch base64 image says only protocvols are allowed – aaa Nov 14 '22 at 11:26
  • this would work and is correct, issue with FormData – aaa Nov 19 '22 at 09:11
0

I want to post my solution that worked, because as I can see on the internet everybody have issues with FormData on nodejs

  1. I was using axios to send the buffer for uploading file
  2. issue is with axios and specially with FormData, it does not add Content-Length in headers, any version of axios does not do this
  3. python API had required Content-Length

If this header becomes optional in python API, the code started to work

The solution is if anybody have similar issues

  • axios does not add Content-Length when working with FormData (could not find any version of axios that works)
  • if you work with a buffer without having the file on local disk than will be issue because of Content-Length
  • if you have the file locally than using the module fs u are able to read the file and add all headers and Content-Length

on axios GitHub issue is saying that this bug is fixed in latest axios, but it was still not working in my case

below is a code by using buffer and Content-Length is not required in 3rd API

    function upload (image: {imageBase64: string, fileName: string}) {
      const { imageBase64, fileName } = image;
      const cleanBase64 = imageBase64.substr(imageBase64.indexOf(',') + 1);
      // buffer should be clean base64
      const buffer = Buffer.from(cleanBase64, 'base64');
    
      const formData = new FormData();
      // filename as option is required, otherwise will not work, will say that received file is string and UploadFile
      formData.append('file', buffer, { filename: fileName });
    
      return client
        .post('url', formData, {
          headers: {
            ...formData.getHeaders(),
          },
        })
        .then((response) => response.data)
        .catch((error) => {
          return {
            status: 'error',
            error,
          };
        });
    }
aaa
  • 446
  • 2
  • 8
  • 29