martinszeltins's avatar

Can't upload a file from node.js to my route, getting empty request

I am trying to upload a local file using NodeJs (the file is in the same directory as app.js) to my remote Laravel server but I'm getting null. For some reason the file doesn't seem to get to the server. I'm not sure what I'm doing wrong..

Here is my node app folder structure

node_app/
├── node_modules/
├── app.js
├── screenshot.jpg
├── package.json

Here is my app.js

const fs = require('fs')
const path = require('path')
const axios = require('axios')
const FormData = require('form-data')
const screenshot = require('screenshot-desktop')

let formData = new FormData()
formData.append('screenshot-file', fs.createReadStream(path.join(__dirname, 'screenshot.jpg')))

axios.post(
  `https://www.myapp.com/api/upload-screenshot`,
  formData,
  { headers: { 'content-type': 'multipart/form-data' } }
)

And here is my endpoint controller

class ScreenshotController extends Controller
{
    public function upload(Request $request)
    {
        // $file is null :(
        $file = $request->file('screenshot-file');

        $filename = 'screenshot.jpg';
        $file->move(storage_path('/uploads/screenshots/'), $filename);

        return $filename;
    }
}

Error: 'Call to a member function move() on null'

When the request arrives at my controller, there doesn't seem to be a 'screenshot-file' file submitted as it is showing null.

Also using dump($request->all()); it returns an empty array [] which means nothing has been submitted.

0 likes
10 replies
Nakov's avatar

@martinzeltin debug what this does then:

fs.createReadStream(path.join(__dirname, 'screenshot.jpg'))

as obviously it is not reading the correct file that you are trying to upload. So use console.log and make sure that that part is correct first. Everything else seems correct.

martinszeltins's avatar

However doing a simple post request, it does arrive at the server. So something must be wrong with my formData...

axios.post(`https://www.myapp.com/api/upload-screenshot`, {
   hello: 'asdf'
})
martinszeltins's avatar

@nakov , that example is using a form upload but I have a local file on my system that I'm trying to upload.

Here is the result of console.log(fs.createReadStream(path.join(__dirname, 'screenshot.jpg')))

ReadStream {
  readable: true,
  _events: [Object: null prototype] { end: [Function] },
  _eventsCount: 1,
  path: 'C:\Users\Martins\Desktop\Programming\node-screenshot\screenshot.jpg',
}

So this seems to work.

But what was strange that this didn't work. I just got an empty [] from $request->all()

let formData = new FormData()
formData.append('test', 'variable')
axios.post(
  `https://www.myapp.com/api/upload-screenshot`,
  formData,
  { headers: { 'content-type': 'multipart/form-data' } }
)
martinszeltins's avatar

@nakov now I'm getting this error

UnhandledPromiseRejectionWarning: ReferenceError: FormData is not defined
Nakov's avatar

@martinzeltin oh, sorry. I now see that you use NodeJS .. what's funny is that even the example for the form-data library shows the way you use the upload:

https://www.npmjs.com/package/form-data

So you say that this is passing:

axios.post(`https://www.myapp.com/api/upload-screenshot`, {
   hello: 'asdf'
})

Have you tried this then:

axios.post(`https://www.myapp.com/api/upload-screenshot`, {
   'screenshot-file': fs.createReadStream(path.join(__dirname, 'screenshot.jpg'))
}, { headers: { 'content-type': 'multipart/form-data' } })

?

martinszeltins's avatar

@nakov The second one returns an empty array [] when doing $request->all()

But the first one submits the hello and asdf

martinszeltins's avatar
martinszeltins
OP
Best Answer
Level 14

@nakov Thank you for all your help!

I finally got it to work after thoroughly reading the documentation for the node.js form-data library. I was passing in the wrong headers, I needed to first get the headers from my formData object and then pass them on to axios like this:

function uploadScreenshot()
{
    let formData = new FormData()
    let stream   = fs.createReadStream(path.join(__dirname, 'screenshot.jpg'))

    formData.append('screenshot-file', stream)

    let formHeaders = formData.getHeaders()

    axios.post('https://www.myapp.com/api/upload-screenshot', formData, {
        headers: {
            ...formHeaders,
        },
    }).catch(error => {
        console.log(error)
    })
}

Please or to participate in this conversation.