Unable to send large payload using fetch in javascript

Viewed 1231

I am a beginner in the JavaScript and NodeJS space. I was trying to create a simple file upload project with NodeJS. Furthermore, I created the routes in NodeJS and capturing the image from the webpage and sending it to the NodeJS routes using fetch. This is the HTML file:

<div class="file-upload">
    <div class="image-upload-wrap">
        <input class="file-upload-input" type='file' onchange="readURL(this);" accept="image/*" />
        <div class="drag-text">
        <h3>Drag and drop a file or select add Image</h3>
        </div>
    </div>
    <div class="file-upload-content">
        <div class="file-upload-display">
            <img id="file-upload-image" class="file-upload-image" src="#" alt="your image" />
            <div class="image-title-wrap">
                <button type="button" onclick="removeUpload()" class="remove-image"><i class="fas fa-trash-alt"></i> Remove <span class="image-title">Uploaded Image</span></button>
            </div>
        </div>
    </div>
</div>
<br>
<div class="file-upload-server d-flex justify-content-center">
    <button class="btn btn-expand-lg btn-primary" onclick="uploadFile()"><i class="fas fa-cloud-upload-alt"></i> Upload</button>
</div>

This is my JavaScript file:

    async function uploadFile() {
    let img = document.getElementById('file-upload-image').src;
    console.log('Image String Length: ' + img.length);
    const payload = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            file: img, 
        })
    }
    console.log(`Payload: ${JSON.stringify(payload)}`);
    
   await fetch('http://localhost:3030/image/uploadimage', payload)
    .then(response => response.json())
    .then(data => {
        console.log('Success:', data);
    })
    .catch((error) => {
        console.error('Error:', error);
    });
}

The image here is the base64 encoded string and that I passing to my nodejs route.

When I click my upload button I get the following error:

enter image description here

I have tested this code with an image of size 5.72kb and it works. But when I try to upload an image of size 81.7kb it fails with that error.

This is the nodejs route:

router.use(imageupload({
   limits: { fileSize: 50 * 1024 * 1024 },
}));

router.use(express.urlencoded({limit: '50mb', extended: true}));
router.use(express.json());

const decodeBase64Image = (dataString) => {
  let matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
  response = {};
  if (matches.length !== 3) {
    return new Error('Invalid input string');
  }
  response.type = matches[1];
  response.data = Buffer.from(matches[2], 'base64');
  return response;
}

router.post('/uploadimage', cors(corsOptions), async (req, res) => {
     let decodedImage = decodeBase64Image(req.body.file); 
     let imageBuffer = decodedImage.data;
     let type = decodedImage.type;
     let extension = mime.getExtension(type);
     let NewImageName =  Math.random().toString(36).substring(7);
     let fileName = 'image-' + NewImageName + '.' + extension;
     try {
           fs.writeFile(`${path.join(__dirname, '../')}/public/uploads/${fileName}`, 
           imageBuffer, function(err) {
           if(err) throw err;
           console.log('The file was uploaded successfully');
           return res.status(200).json({status: 'success', message: 'File uploaded successfully'});
    });
    } catch(err) { 
          console.log(err);
          return res.status(500).send(err);
    }
});

Any help or guidance around this would be great.

2 Answers

You need to set an upload limit for your express.json() middleware since you are using 'Content-Type': 'application/json' header in the post request.

Try replacing router.use(express.json()) with router.use(express.json({ limit: '50mb'})

Documentation

Edit

The error Unexpected token < in JSON at position 0' happens if you try to parse a string with response.json() which is not formatted as json. You can reproduce it in a one liner in nodejs as JSON.parse('I am no json...')

If you call response.json() on the client , it has to be properly formatted json or it result in an error. For doing so, use res.json() instead of res.send().

You should also make sure to always respond to the client, so change the line if(err) throw err; to

if(err) { 
  console.error(err)
  // always send back a status, 500 means server error.. 
  res.status(500).json('Internal server error')
  return
}

I would suggest that you should use multer here's a link

Backend Code Sample:

var multer = require('multer')
var signature = multer.diskStorage({
destination: function (req, file, cb) {
    cb(null, './public/')
},
filename: function (req, file, cb) {
    if (file.mimetype == 'image/jpeg' || file.mimetype === 'image/jpg' || file.mimetype === 'image/png' || file.mimetype === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
        cb(null, Date.now() + file.originalname)
    } else {
        cb({ error: 'Image type not supported' })
    }
}
})

var uploadSignature = multer({ storage: signature })

router.put('/updateWorkOrderForSign',uploadSignature.array('signature', 1), function (req, res) {

try {
if (req.body.signature) {
var base64Data = req.body.signature.replace(/^data:image\/png;base64,/, "");

 let rString = Math.random().toString(36).substring(7);
 let date = Date.now();
 let imageName = '/' + date + rString + '.png';
 let imagePath = '/public' + imageName;

 require("fs").writeFile('./' + imagePath, base64Data, 'base64', function (err) {
  if (err) {
      log.error('SAVE IMAGE FAILED WITH ERROR: ', err);
   } else {
      log.info('Signature image saved successfully')
   }
  });
  } else {
          //nothing to do
          log.error("No signature image present")
  }

  } catch (err) {
          log.error('ERROR WHILE CONVERTING BASE64 TO IMAGE: ', err);
  }
})

Front-end code sample(I have implemented this in react native):

async function uploadFile() {
    let imageObject = {
              uri: image.path,
              type: 'image/png',
              name: 'signatureImage'
             }

    var form = new FormData();
        form.append('signature', imageObject)
        form.append(‘task’Id, taskId);

    await fetch(urlStr, {
                          method: 'PUT',
                          headers: {
                                  'Authorization': bearerToken,
                                   Accept: 'application/json',
                                  'Content-Type': 'multipart/form-data'},
                          body: params,
                })
                  .then(response => response.json())
                  .then(responseData => {
                        var result = JSON.stringify(responseData);
                        return result;
                   })
                  .catch(error => {
                       console.log('ERROR WHILE UPLOADING IMAGE: ',error)
                  });
}
Related