Hash a file and a directory structure in browser without using the network

Hi!

I’m trying to hash a file and directory structure in plain JavaScript running in the browser, and would like this to work offline. Also, when online I’d rather not spin up a node that automatically sends messages on the network, the operation should be entirely local. Since this is the only IPFS-related task that will be performed, the smaller the dependencies, the better.

This question has already been asked here but I wasn’t sure if necroposting was okay here.

I followed the chain of function calls starting from js-ipfs, and found that it calls the dagBuilder function from ipfs-unixfs-importer which seems to be the point in the call stack where some actual work gets done.

Unfortunately, ipfs-unixfs-importer does not seem to be a module that can be compiled for the browser (e.g. it doesn’t exist on a CDN, only the ipfs-unixfs library exists for browsers, and seems to require manually chunking the file.

In the existing question by @sparkplug0025, someone suggested using GitHub - alanshaw/ipfs-only-hash: #️⃣ Just enough code to calculate the IPFS hash for some data, but that also depends on ipfs-unixfs-importer and therefore also seems to be incompatible with browsers (the ipfs-only-hash package isn’t available on a CDN either).

1 Like

I think it’s totally fine as long as it’s the same topic (IMO this is), but it also doesn’t matter too much :slight_smile:.

I really like this question as it comes up somewhat frequently, but you’ve made me realise I don’t actually know the solution in-browser. Sorry I don’t have an immediate answer for you.

In the meantime, if you don’t want js-ipfs to communicate with anything else, I’m assuming nulling out the bootstrap list would prevent it from talking:

await Ipfs.create({Bootstrap: []});

I built ipfs-unixfs-importer (turns out it works fine in browser), and then made one very minor change to ipfs-only-hash to produce this mini-demo:

(live)

<html><head>
<script src="https://ipfs.io/ipfs/bafkreiarihsbhiyqwyqcncl2ufxvgdsxjo34mmuedutmikdf4gyqpbenlq"></script>
<script>
const importer = IpfsUnixfsImporter.importer;

const block = {
	get: async cid => { throw new Error(`unexpected block API get for ${cid}`) },
	put: async () => { throw new Error('unexpected block API put') }
}

async function ipfsOnlyHash(content, options) {
	options = options || {}
	options.onlyHash = true

	if (typeof content === 'string') {
		content = new TextEncoder().encode(content)
	}

	let lastCid
	for await (const { cid } of importer([{ content }], block, options)) {
		lastCid = cid
	}

	return `${lastCid}`
}

async function updateCID(ev) {
	let out = document.getElementById("cid");
	out.innerHTML = await ipfsOnlyHash(ev.target.value);
}
</script>
</head>

<body>
<form><textarea id="txt" oninput="updateCID(event);"></textarea></form>
<span id="cid"></span>
</body></html>

thanks for the awesome information.