Reputation: 923
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");
});
});
}
Upvotes: 2
Views: 1629
Reputation: 6377
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).
Upvotes: 0
Reputation: 4911
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
Upvotes: 1