1

Hey Guys an express app which gets a file via Post and should than save it, send it to another server and delete it afterwards. This whole process works but if i log the steps the programm produces i'm confused because i get a outprint for delete even before the file is saved. So for now i want to know if there is the possibility to really call the 3 functions step by step. Because deleting a file before it is saved isnt right. so here is my code and i hope you can help me.

server.post("/upload", function (req, res) {
    if (!req.files) {
        res.send("No file uploaded");
    } else {
        //the Filepointer points to the whole File object which can contain multiple files

        var filePointer = req.files;
        console.log(filePointer);
        res.send("Files uploaded");
        let key;
        for (key of Object.keys(filePointer)) {
            //single file uploaded so no json Object else get number of elements in the Array and loop over them
            if ((typeof filePointer[key][0]) === 'undefined') {
                //if theres only one File in the Object you can directly access the keys
                var file = filePointer[key];
                //console.log("var is undefined");
                console.log(file["name"]);
                var uploadPath = __dirname + "/uploads/" + file["name"];
                console.log(uploadPath);
                saveFile(uploadPath, file);
                sendToTika();
                deleteFile(uploadPath);
            } else {
                let index;
                for (index of Object.keys(filePointer[key])) {
                    //with multiple files you need to access the keys with an indey i
                    var file = filePointer[key][index];
                    console.log(file["name"]);
                    var uploadPath = __dirname + "/uploads/" + file["name"];
                    console.log(uploadPath);
                    saveFile(uploadPath, file);
                    sendToTika();
                    deleteFile(uploadPath);
                }
            }
        }
    }

});

server.listen(3000, function () {
    console.log("server is listening on port 3000");
})

function saveFile(UploadPath, file) {
    file.mv(UploadPath, function (err) {
        if (err) {
            console.log(err);
        } else {
            console.log("file uploaded and saved" + UploadPath);
        }

    })
}

function sendToTika() {
    console.log("tika Function");
}
//TODO: check if sync or async is better for this task
function deleteFile(deletePath) {
    fs.unlink(deletePath, function (error) {
        if (error) {
            console.log(error);
        }
        console.log("deleted" + deletePath);
    })
}

the console log is:

tika Function
deleted
file uploaded and saved

EDIT: Hey Guys first of all thank you all for your answers i will take a look at promises!

Henry Sachs
  • 192
  • 18
  • It is possible using promises because those functions are asynchronous. Take a look here : https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Serge K. Oct 04 '17 at 09:36
  • You should read up on async programming in Javascript. Your `saveFile` method calls `file.mv` and immediately returns... later on, after the async file move is completed, the function you passed (which logs "file uploaded and saved") is triggered... likely AFTER `fs.unlink` has already completed and run ITS callback – Alex McMillan Oct 04 '17 at 09:37
  • @Henry In this case you are not waiting or you are not using any promises. If you use promise with a callback you can achieve this – Suvethan Nantha Oct 04 '17 at 09:38

2 Answers2

1

The problem is the functions saveFile and deleteFile use an asynchronous function (the callback) you can use Promise :

function saveFile(UploadPath, file) {
  return new Promise(function(resolve, reject) {
    file.mv(UploadPath, function (err) {
      if (err) {
        reject(err);
        console.log(err);
      } else {
        resolve();
        console.log("file uploaded and saved" + UploadPath);
      }
    });
  });
}


function deleteFile(deletePath) {
  return new Promise(function (resolve, reject) {
    fs.unlink(deletePath, function (error) {
      if (error) {
        reject(error);
        console.log(error);
      }
      else {
        resolve();
        console.log("deleted" + deletePath);
      }
    });
  });
}

Use the same logic for the sendToTika function and call the function like that:

saveFile(uploadPath, file).then(() => {
  return sendToTika();
}).then(() => {
  return deleteFile(uploadPath);
}).catch((err) => {
  // get the error here
})

You can also use the function util.promisify to returns a version of function that returns promises : https://nodejs.org/api/util.html#util_util_promisify_original

Arkerone
  • 1,971
  • 2
  • 22
  • 35
  • in this case what happens if sendToTika method gets failed? still it will call the deleteFile method which is wrong – Suvethan Nantha Oct 04 '17 at 09:49
  • In his code the sendToTika function just call a console.log but he can use a promise in this function. I've edited my answer. – Arkerone Oct 04 '17 at 09:50
  • As far as I think, still there is a small conflict with your answer and expected answer please read the question again and answer it according to it – Suvethan Nantha Oct 04 '17 at 10:10
1

Try this

function saveFile(UploadPath, file) {
  return new Promise(function(resolve, reject) {
    file.mv(UploadPath, function (err) {
      if (err) {
        reject(err);
        console.log(err);
      } else {
        resolve(true);
        console.log("file uploaded and saved" + UploadPath);
      }
    });
  });
}


function deleteFile(deletePath) {
  return new Promise(function (resolve, reject) {
    fs.unlink(deletePath, function (error) {
      if (error) {
        reject(error);
        console.log(error);
      }
      else {
        resolve(true);
        console.log("deleted" + deletePath);
      }
    });
  });
}

Finally,

router.post('/upload', function(req, response){
    saveFile(uploadPath, file).then((status) => {
          if(status){
              sendToTika().then((result) => {
                if(result){
                    deleteFile(uploadPath);
                    response.send({"status": true, "error": ''}) ;
                }
                else
                    response.sendStatus(203);
              }).catch((err) => {
                response.sendStatus(203);
              })
          }
          else
            response.sendStatus(203);
        }).catch((err) => {
          response.sendStatus(203);
        })
 });

I hope this help you.

Suvethan Nantha
  • 2,404
  • 16
  • 28
  • 1
    Hey thanks your answer was a bit better structured thats why i ticked it as correct for persons running in this problem later. – Henry Sachs Oct 04 '17 at 10:28
  • 1
    with your code like this i get the error (node:5735) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent. (node:5735) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. could it be that it is linked with the package i'm using because it is already using promises (take a look: https://github.com/richardgirges/express-fileupload) – Henry Sachs Oct 04 '17 at 10:46
  • 1
    so i think i found it already the problem was that i was already sending a response earlier. Also i need to add the promise for the Tika function. I will post the final code when finishing this. – Henry Sachs Oct 04 '17 at 10:54