Which is the simplest way to compare a hash of a file without storing it in a database?
For example:
var filename = __dirname + '/../public/index.html';
var shasum = crypto.createHash('sha1');
var s = fs.ReadStream(filename);
s.on('data', function(d) {
shasum.update(d);
});
s.on('end', function() {
var d = shasum.digest('hex');
console.log(d + ' ' + filename);
fs.writeFile(__dirname + "/../public/log.txt", d.toString() + '\n', function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved!");
}
});
});
The above code returns the hash of the HTML file. If I edit the file how can I know if it has been changed? In other words, how can I know if the hash has been changed?
Any suggestions?
Edited
Now the hash is being saved in the log file. How can I retrieve the hash from the file and match it with the new generated one? A code example would be awesome to give me a better understanding.
There is no difference with this question, but it isn't clear for me yet how to implement it.
If you're looking for changes on a file, then you can use one of Node's filesystem functions, fs.watch. This is how it's used:
fs.watch(filename, function (event, filename) {
//event is either 'rename' or 'change'
//filename is the name of the file which triggered the event
});
The watch function is however not very consistent, so you can use fs.watchFile as an alternative. fs.watchFile uses stat polling, so it's quite a bit slower than fs.watch, which detects file changes instantly.
Watching a file will return an instance of fs.FSWatcher, which has the events change and error. Calling .close will stop watching for changes on the file.
Here's an example relating to your code:
var filename = __dirname + '/../public/index.html';
var shasum = crypto.createHash('sha1');
var oldhash = null;
var s = fs.ReadStream(filename);
s.on('data', function(d) {
shasum.update(d);
});
s.on('end', function() {
var d = shasum.digest('hex');
console.log(d + ' ' + filename);
oldhash = d.toString();
fs.writeFile(__dirname + "/../public/log.txt", d.toString() + '\n', function(err) {
if(err) {
console.log(err);
}
else {
console.log("The file was saved!");
}
});
});
//watch the log for changes
fs.watch(__dirname + "/../public/log.txt", function (event, filename) {
//read the log contents
fs.readFile(__dirname + "/../public/log.txt", function (err, data) {
//match variable data with the old hash
if (data == oldhash) {
//do something
}
});
});
What's the difference between this question and the previous one you asked? If you're not wanting to store it in a database, then store it as a file. If you want to save the hash for multiple files, then maybe put them in a JSON object and write them out as a .json file so they're easy to read/write.
EDIT
Given what you added to your question, it should be pretty simple. You might write a function to do check and re-write:
function updateHash (name, html, callback) {
var sha = crypto.createHash('sha1');
sha.update(html);
var newHash = sha.digest('hex');
var hashFileName = name + '.sha';
fs.readFile(hashFileName, 'utf8', function (err, oldHash) {
var changed = true;
if (err)
console.log(err); // probably indicates the file doesn't exist, but you should consider doing better error handling
if (oldHash === newHash)
changed = false;
fs.writeFile(hashFileName, newHash, { encoding: 'utf8' }, function (err) {
callback(err, changed);
});
});
}
updateHash('index.html', "<html><head><title>...", function (err, isChanged) {
// do something with this information ?
console.log(isChanged);
});
Related
I have a file dmreboot_service.js in my /js folder. When I run this file using node /js/dmreboot_service.js it successfully invokes a direct method in Azure.
I need to be able to execute this function or file on a button click from my web app.
I tried loading the script into the head of my html using :
<script src="js/dmreboot_service.js"></script>
I put an alert in the external file.
If I put this alert at the top of the file it works, but at
the bottom it fails, so the contents of the file are not loading.
The content of dmreboot_service.js is :
'use strict';
var Registry = require('azure-iothub').Registry;
var Client = require('azure-iothub').Client;
var connectionString ="HostName=XxxxxxxX.azure-devices.net;SharedAccessKeyName=service;SharedAccessKey=XxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX=";
var registry = Registry.fromConnectionString(connectionString);
var client = Client.fromConnectionString(connectionString);
var deviceToReboot = 'Runner';
var startRebootDevice = function (twin) {
var methodName = "reboot";
var methodParams = {
methodName: methodName,
payload: null,
timeoutInSeconds: 30
};
client.invokeDeviceMethod(deviceToReboot, methodParams, function(err, result) {
if (err) {
console.error("Direct method error: "+err.message);
} else {
console.log("Successfully invoked the device to reboot.");
}
});
};
var queryTwinLastReboot = function() {
registry.getTwin(deviceToReboot, function(err, twin){
if (twin.properties.reported.iothubDM != null)
{
if (err) {
console.error('Could not query twins: ' + err.varructor.name + ': ' + err.message);
} else {
var lastRebootTime = twin.properties.reported.iothubDM.reboot.lastReboot;
console.log('Last reboot time: ' + JSON.stringify(lastRebootTime, null, 2));
}
} else
console.log('Waiting for device to report last reboot time.');
});
};
startRebootDevice();
setInterval(queryTwinLastReboot, 2000);
alert('dmreboot included!');
I have also tried creating a function in the head of my html that includes the entire contents of dmreboot_service.js, but although the function is called successfully the code does not execute.
This is the last part of a project that I need to get working. I'm fairly new to this, and this is driving me nuts!! Any advice much appreciated.
I usually handle click in HTML with javascript like so;
const doSomething = document.querySelector('.whateverclassnameyouchoose').addEventListener('click',onClick);
function onClick(e){
what ever you want the function to do
}
Hope it helps :)
I wrote the following code to create a hash of a file:
function hashFile(filePath){
try{
// Setup hash
var hash = crypto.createHash('sha256');
hash.setEncoding('hex');
// Setup filestream
var fileStream = fs.createReadStream(filePath);
fileStream.pipe(hash, { end: false });
fileStream.on('end', function () {
// Get the hash
hash.end();
var thisHash = String(hash.read());
});
}catch (err) {
console.log( "Error thrown : " + err );
return;
}
}
This worked just fine until I threw some smaller files at it. When I did this the function would just hang. The filestream.on('end') callback would never happen.
I rewrote it to not use createReadStream:
function hashFile(filePath){
try{
fs.readFile(filePath, function (err, data) {
// Make the hash
var thisHash = crypto
.createHash('sha256')
.update(data, 'utf8')
.digest( 'hex');
console.log(thisHash);
});
}catch (err) {
console.log( "Error: " + err );
return;
}
}
This code works just fine, except it's incredibly slow on files that are a few MB or larger.
My question is why doesn't the first function work on small files?
So after searching for the answer all evening, I figured it out 15 min after writing this post. The 'end' callback needs to configured before calling pipe:
var fileStream = fs.createReadStream(filePath);
var hash = crypto.createHash('sha256');
hash.setEncoding('hex');
fileStream.on('end', function () {
hash.end();
console.log(hash.read());
});
fileStream.pipe(hash);
I have the following function to look for changes in a file, open the file and read the said changes. For this I use fs.statSync to calculate the length of my buffer.
var allFiles=[logfile]
allFiles.forEach (function (file) {
var fName = file;
var fNameStat = new Object;
fNameStat = fs.statSync(fName);
if (!fNameStat.isFile()) {
console.log(fName + ' is not a file');
process.exit(1);
}
console.log('watching ' + fName + ' bytes: ' + fNameStat.size);
fs.watch(fName, function (event, filename) {
var fNameStatChanged = fs.statSync(fName);
console.log('file changed from ' + fNameStat.size + ' to ' + fNameStatChanged.size);
fs.open(fName, 'r', function(err, fd) {
var newDataLength = fNameStatChanged.size - fNameStat.size;
var buffer = new Buffer(newDataLength, 'utf-8');
fs.readSync(fd, buffer, 0, newDataLength, fNameStat.size, function (err, bytesRead, newData) {
if (err) {
console.log(err);
};
logline = newData.toString()
console.log(logline);
socket.emit("log-tail",logline);
});
fNameStat = fs.statSync(fName);
});
}); // fs.watch
}); // allFiles.forEach
But I get a buffer outofbounds error. This happens when the log file gets two messages one after the other.
file changed from 40756 to 40789
file changed from 40756 to 40844
fs.js:620
binding.read(fd, buffer, offset, length, position, req);
^
Error: Offset is out of bounds
I am assuming its to do with the asynchronous calls. The file changes before the first read operation is complete and as such the buffer is wrong. But even after using the corresponding synchronous calls the code doesn't work. Any ideas?
Currently i'm reading and writing files asynchronously, the thing is that i'm not sure that all the line from the file were read before the rest of my code is executed, here is my code:
var fs = require('fs');
//var str = fs.readFileSync("stockststs.json");
function updateJson(ticker) {
//var stocksJson = JSON.parse(fs.readFileSync("stocktest.json"));
fs.readFile('stocktest.json', function(error, file) {
stocksJson = JSON.parse(file);
if (stocksJson[ticker]!=null) {
console.log(ticker+" price : " + stocksJson[ticker].price);
console.log("changin the value...")
stocksJson[ticker].price = 989898;
console.log("Price after the change has been made -- " + stocksJson[ticker].price);
fs.writeFile('stocktest.json',JSON.stringify(stocksJson, null, 4) , function(err) {
if(!err) {
console.log("File successfully written");
}
});
}
else {
console.log(ticker + " doesn't exist on the json");
}
});
}
updateJson("APPL");
I'm wondering if there is any better way to to implement ?
its always a good practice to check in your callback for error. For example, i have written a small getCache function that checks if the file exists first, then tries to read that file and fire a callback afterwards
Cache.prototype.getCache = function(cache_filename, callback) {
cache_filename = this.cache_foldername + cache_filename;
fs.exists(cache_filename, function(exists) {
if (exists) {
fs.readFile(cache_filename, 'utf8', function (error, data) {
if (!error) {
console.log('File: '+ cache_filename +' successfully Read from Cache!');
_.isObject(data) ? callback(null,data) : callback(null,JSON.parse(data));
} else console.log("Error Reading Cache from file: " + cache_filename + " with Error: " + error);
});
} else callback(null,false);
});
}
note that in here what you need to do is to write the file after it has been read and checked with the if (!error)
i hope this helps
I am getting confuse how to read a list of files recursively.
Assume I have 3 text files in my filesytem api root directory
text1.txt
text2.txt
text3.txt
My goal is to read each text files one by one and then concatenate all the entries in each file into one string but I am currently at lost
how to do this in Javascript FileSystem API.
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
function onInitFs(fs) {
var dirReader = fs.root.createReader();
var entries = [];
// Call the reader.readEntries() until no more results are returned.
var readEntries = function() {
dirReader.readEntries (function(results) {
if (!results.length) {
readAllFiles(results);
} else {
entries = entries.concat(toArray(results));
readEntries();
}
}, errorHandler);
};
readEntries(); // Start reading dirs.
}
function readAllFiles(entries){
//Loop thru all the files and read each entries
}
I have seen how to read one text file but I dont know how to implement the reading of all files and concatenate the value.
They all implement callback functions so I am getting confused on how to handle it. Any points please?
I actually have been basing all my works in this http://www.html5rocks.com/en/tutorials/file/filesystem
UPDATE 2
As per #Johan
I actually changed my code to make use of callback
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
function onInitFs(fs) {
var dirReader = fs.root.createReader();
var entries = [];
// Call the reader.readEntries() until no more results are returned.
var readEntries = function() {
dirReader.readEntries (function(results) {
if (!results.length) {
readAllFiles(results, concatMessages);
} else {
entries = entries.concat(toArray(results));
readEntries();
}
}, errorHandler);
};
readEntries(); // Start reading dirs.
}
var concatMessage = '';
function concatMessages(message){
concatMessage += message;
}
function readAllFiles(logs, callBack) {
logs.forEach(function(entry, iCtr) {
var message;
entry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
//message += this.result;
if(callBack)
callBack('==================' + iCtr + '==========================');
callBack(this.result);
};
reader.readAsText(file); // Read the file as plaintext.
}, function(err) {
console.log(err);
});
});
}
My only problem is this, the callback function is not sequential.
It reads text2.txt first then text3.txt then text1.txt so the end result is not sequential which is not what I want to do. Any more hints?
Highly recommend you consider using something like caolan's async library to accomplish this.
You can do something like this:
async.each(openFiles, function(file, callback) {
// Perform operation on file here.
console.log('Processing file ' + file);
callback();
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if (err) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('A file failed to process');
} else {
// do your concatenation here
}
});