I'm implementing an image cache for favicons found on the web. At one point I have the favicon's URL, and I'm sending it to my page, everything is fine.
Now alongside this, I write the icon to disk, using a pseudo-unique filename that I construct using the URL components.
And this works too, but only if I write to /tmp/
!?
Here is my code:
router.get('/favicon', function(req, res) {
favicon(req.query.url, function(err, iconUrl) {
if (iconUrl) {
var u = Url.parse(iconUrl);
request.get({url: iconUrl}, function (err, response, body) {
if (!err && typeof body !== 'undefined') {
var h = u.host.replace(/\//g, ''),
p = u.path.replace(/\//g, ''),
fileName;
fileName = path.join('/tmp', h + '.' + p);
// fileName = path.join(__dirname, '..', 'cache', h + '.' + p);
fs.writeFile(fileName, body, function(err) {
if(err)
console.log(err);
else
console.log("SAVED! (%s)", fileName);
});
} else {
console.log('### ERROR iconUrl: (%s) err: (%s)', iconUrl, err);
}
});
res.send(iconUrl);
} else {
res.status(500).send('No icon found');
}
});
});
This is called on the client (web browser) side like this:
$.get("/favicon", {
url: decodeURI(feedHost),
dataType: "json",
timeout: 2000
}, function(iconURL) {
console.info('OK (icon: %s)', iconURL);
}).done(function(iconURL) {
console.info('OK (icon: %s)', iconURL);
}).fail(function() {
console.error('ERROR');
});
I though it was a "disk full" problem but no, here is the partition table on this machine, the project directory (where my image cache directory resides) in inside the /home
partition, and /tmp/
is, well, on /
:
# df -h
Filesystem Size Used Avail Use% Mounted on
udev 1.9G 0 1.9G 0% /dev
tmpfs 388M 6.3M 382M 2% /run
/dev/sda6 19G 14G 3.8G 79% /
tmpfs 1.9G 23M 1.9G 2% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/loop0 82M 82M 0 100% /snap/core/4206
/dev/loop1 128M 128M 0 100% /snap/shotcut/20
/dev/loop2 82M 82M 0 100% /snap/core/4110
/dev/loop3 82M 82M 0 100% /snap/core/4017
/dev/loop4 298M 298M 0 100% /snap/shotcut/17
/dev/loop5 128M 128M 0 100% /snap/shotcut/21
/dev/sda5 121G 102G 14G 89% /home
tmpfs 388M 12K 388M 1% /run/user/1000
So when I use the code above, everything works fine, and /tmp/
fills with images, asynchronously, perfect.
As soon as I remove the leading /
of the file path - pointing to a valid path, of course - then one, two, maximum 3 favicons are copied and then the process fails somehow, triggering the .fail()
method one the client side, but without any error on the server.
Help!
Questions:
- What could be causing this weird "write 2 or 3 files then stop" problem? NB: The server is launched with PM2.
- Is my ajax call (JQuery
$.get()
) correct? Why does it make the other ajax calls in the page fail, instead of gently erroring and then move on?
UPDATE
It turns out that I was not saving a correct image file in /tmp/
; I am now using this code:
router.get('/favicon', function(req, res) {
favicon(req.query.url, function(err, iconUrl) {
if (iconUrl) {
if (!iconUrl.startsWith('..') && Url.parse(iconUrl)) {
var u = Url.parse(iconUrl),
h = u.host.replace(/\//g, ''),
p = u.path.replace(/\//g, ''),
fileName;
fileName = path.join('/tmp/cache', h + '.' + p);
// fileName = path.join(__dirname, '..', 'cache', h + '.' + p);
let stream = fs.createWriteStream(fileName);
stream.on('finish', function () {
console.log("SAVED %s (%s)", fileName, iconUrl);
}).on('error', function (err) {
console.log("NOT SAVED %s (%s)", fileName, err);
});
request(iconUrl).pipe(stream);
} else {
console.log('iconUrl NOT OK: (%s)', iconUrl);
}
res.send(iconUrl);
} else {
res.status(500).send('No icon found');
}
});
});
And now the files saved are proper image files ; However the problem persists, and I have now tested this code on several other (Linux) machines including a production web server, same thing! No way to save (more than a handful of) files within the project repo, and yet if I use /tmp/*
as the file path, everything is fine!
This is rendering me crazy, please put me out of this misery :|
Update 2
I managed to write to my home partition. The only place I cannot write to is any directory inside (and including) the project directory.