2

I have a form that has a field that can upload multiple images in a single <input> tag. When I access the filesystem using Node, it seems to queue the callback for reading/writing files asynchronously. Because I have multiple files, I have these calls in a for loop, so the value of i is always array.length by the time the callbacks are hit, causing the object to be undefined.

for (var i = 0; i < req.files.photos.length; i++) {
    req.fs.readFile(req.files.photos[i].path, function(err, data) {
        if(err) throw err;

        // i = req.files.photos.length here
        // Test is undefined when the breakpoint on this line is hit for the first time
        var test = req.files.photos[i];

        // Both print "undefined"
        console.log(test.name);
        console.log(test.originalFileName);

        var newPath = __dirname + "/../public/uploads/" + req.files.photos[i].name;

        req.fs.writeFile(newPath, data, function (err) {
            if (err) throw err;

            console.log("it worked");
        });
    });
}

2 Answers 2

1

You can use an IIFE (Immediately Invoked Function Expression) to capture the correct value of i during each iteration of the for-loop:

for (var i = 0; i < req.files.photos.length; i++) {
    (function(j) {
        req.fs.readFile(req.files.photos[j].path, function(err, data) {
            if(err) throw err;
            var test = req.files.photos[j];

            console.log(test.name);
            console.log(test.originalFileName);

            var newPath = __dirname + "/../public/uploads/" + req.files.photos[j].name;

            req.fs.writeFile(newPath, data, function (err) {
                if (err) throw err;
                console.log("it worked");
            });
        });
    }(i));
}

By invoking this function immediately, the value of i will be captured at its current value and stored as a new reference (j) within the function because i is a primitive value. This is a classic example of scope-chain and closure syntax, there's plenty more examples online if you're still having issues

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this resolved the problem! I have used these before when I had async issues with ajax, guess I should keep this closer in mind next time :)
0

The simplest way is actually to switch to using async/await with promises. If you need to do both things concurrently you can use Promise.all.

It takes a little work to get used to promises or set up with async/await but it is 100% worth it.

import pify from 'pify';
import {readFile, writeFile} from 'fs';

const readFilePr = pify(readFile);
const writeFilePr = pify(writeFile);

async function copyFiles(req) {
 const {photos} = req.files;

  for (let photo of photos) {
    try {
      const image = await readFilePr(photo.path);
      const newPath = `${__dirname}/../public/uploads/${photo.name}`;
      await writeFilePr(newPath);    
    } catch (e) {
      console.error("Problem copying download: ",e);
    }     
  }
}

You may need to set up babel for all of that code to work (assuming there are no typos or anything, I haven't tested it).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.