1
  1. Say I have this <input type="file" id="theFile">

  2. I access the selected file with with myVar = document.getElementById('theFile').value

  3. The output is console.log(myVar); // C:\fakepath\xxx.txt

Then I do a simple POST, there are many ways to do a POST but one basic JavaScript way is:

var http = new XMLHttpRequest();
http.onreadystatechange = function() { 
http.open("POST", "http://localhost:port/upload-test");
http.setRequestHeader('Content-type', 'application/json');
var body = {
    file: document.getElementById('theFile').value
}
http.send(JSON.stringify(body));

Then in my Nodejs Express service I do this:

app.post('/upload-test', (request, response) => {
    console.log(request.body); // { file: 'C:\\fakepath\\xxx.txt' }
});

Now how do I save that file to the pc the service is running on?

I know of other solutions like https://www.w3schools.com/nodejs/nodejs_uploadfiles.asp but this requires the user to use a <form> which is not what I want. I want to upload a file using a basic POST and JSON approach.

Paul Kruger
  • 2,094
  • 7
  • 22
  • 49
  • You are actually not sending the file, only its path, the server-side cannot do anything with it as it has no access to the client's file system. If you want to avoid a form, you can use `formData` and append the file like in [this answer](https://stackoverflow.com/a/53841884/7393478) – Kaddath Apr 26 '19 at 08:13
  • possible duplicate https://stackoverflow.com/questions/17981677/using-post-data-to-write-to-local-file-with-node-js-and-express – Ilarion Halushka Apr 26 '19 at 08:17
  • I've done this, but not in the way you're trying. Firstly, you need to 'read' the contents of the client-side file into a var, then JSON.stringify as you have done. On the server, you then use the fs package to create a new empty file and 'stream' the file contents to the file and save. – Chris Adams Apr 26 '19 at 08:27
  • @ChrisAdams Could you post an example of this? And would this approach work with any file type (not just `txt`)? – Paul Kruger Apr 26 '19 at 08:29
  • I can try, and yes, it should work with any file type. – Chris Adams Apr 26 '19 at 08:31
  • My example is written in React, so my first question would be can you console.log the data within the value? You have let something = document.getElementById('theFile').value. Can you see the file data? – Chris Adams Apr 26 '19 at 08:39
  • Check the last bit of code in my question. `document.getElementById('theFile').value` is `C:\\fakepath\\xxx.txt` – Paul Kruger Apr 26 '19 at 08:45
  • Gotcha, I saw that, but wanted to check if you've managed to read the file contents, but no matter, I've added something into the answer i'm writing now. – Chris Adams Apr 26 '19 at 09:06

3 Answers3

1

The content-type of the request should be multipart/form-data, cos you are uploading the file. you can use multerjs for nodejs upload

var express = require('express')
var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })

var app = express()

app.post('/upload-test', upload.single('avatar'), function (req, res, next) {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
})
Jairo Malanay
  • 1,327
  • 9
  • 13
0

Client Side Firstly, you need to 'read' the file contents into a variable. You cannot upload a file directly as mentioned elsewhere on here.

I'm not sure what you're using client-side. E.g. React, Angular or plain Javascript. So, here's a link to another article on SO about how to select a client-side file and read its content into a var. Read Client-Side file

Once you have the file content in a var, you can 'post' that. E.g.

// Build the Headers object.
let headers = new Headers();
headers.append("Content-Type","application/json");

// Build the body object.
let body = {
    fileName: varContainingFileName,
    fileContent: varContianingContentsOfFile
};

// Build the request
const response = await fetch(URL, {method: 'POST',
                                    headers:headers, 
                                    body:JSON.stringify(body)});

// See what we get back from the service.  Note, for the await to work,
// Needs to be wrapped in async method.
console.log(response);

Server-Side I'm assuming you have set up your route for the API, so straight in....

const fs = require('fs');

uploadFile(req, res) {

    const requestBody = JSON.parse(req.body);

    log.info("Received File Upload request.  File " + requestBody.fileName);

    // If one doesn't exist, create a folder for the file
    fs.existsSync("./SomeFolder/") || fs.mkdirSync("./SomeFolder/");

    // Using method.then(otherMethod) with promises.
    writeSourceDataToFile (requestBody.fileName,
                            "./SomeFolder/",
                            requestBody.fileContent)
      .then(v => finalMethod(v, requestBody.fileName, "./SomeFolder/", res))
      .catch(err => returnError(res, err) );

}

function writeSourceDataToFile (fileName, path, content) {

    return new Promise((resolve, reject) => {

        // User fs write stream to stream the file content var to file.
        let writeSource = fs.createWriteStream(path + fileName)
            .on('end', function () {
                log.info("File wrote to disk - End event");
                resolve (true);
            })
           .on('finish', function () {
               log.info("File wrote to disk - Finish event");
               resolve (true);
            })
           .on('error', function (err) {
               log.info('Error whilst writing file - ' + fileName);
               reject (err);
           });

        writeSource.write(content);

        writeSource.end();

    });
}

function finalMethod(v, fileName, path, res) {

    // Final method just wraps things up and sends some message back to 
    // client.

    log.info(fileName + " File Upload Process Completed.");
    res.write("File Upload Process Completed.\n");

    res.status(200).end();

}

// Something went wrong, log it and tell the client.
function returnError(res, err) {
  log.error(err);
  res.status(500).send(err);
}
Chris Adams
  • 1,376
  • 1
  • 8
  • 15
0

You can use Multer module for file upload

const multer = require('multer');

Now set destinatoin folder to save the upladed files

const upload = multer({ dest: './uploads/' });

finally use upload.single() for single file upload or upload.array() for multiple file uploads

app.post('/upload-test',upload.single('myFile'),(request, response) => {
    console.log(request.body);
});
Subha
  • 31
  • 3