diff --git a/README.md b/README.md index 89839f78..f7d8fd97 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,33 @@ ipfs.files.createAddStream(function (err, stream) { + +### `cat` + +> Streams the file at the given IPFS multihash.. + +##### `Go` **WIP** + +##### `JavaScript` - ipfs.cat(multihash, [callback]) + +`multihash` is a [multihash][] which can be passed as + +- Buffer, the raw Buffer of the multihash +- String, the base58 encoded version of the multihash + +`callback` must follow `function (err, stream) {}` signature, where `err` is an error if the operation was not successful and `stream` is a readable stream of the file. + +If no `callback` is passed, a promise is returned. + +```js +ipfs.files.cat(multihash, function (err, file) { + // file will be a stream containing the data of the file requested +}) +``` + + + + ## Object ### `object.new` diff --git a/src/files.js b/src/files.js index a33cc6c6..52b8a376 100644 --- a/src/files.js +++ b/src/files.js @@ -7,9 +7,10 @@ const Readable = require('readable-stream') const path = require('path') const fs = require('fs') const isNode = require('detect-node') +const bl = require('bl') module.exports = (common) => { - describe('.files/add', () => { + describe('.files', () => { let smallFile let bigFile let ipfs @@ -31,121 +32,227 @@ module.exports = (common) => { common.teardown(done) }) - it('stream', (done) => { - const buffered = new Buffer('some data') - const rs = new Readable() - rs.push(buffered) - rs.push(null) + describe('.add', () => { + it('stream', (done) => { + const buffered = new Buffer('some data') + const rs = new Readable() + rs.push(buffered) + rs.push(null) - const arr = [] - const filePair = {path: 'data.txt', content: rs} - arr.push(filePair) + const arr = [] + const filePair = {path: 'data.txt', content: rs} + arr.push(filePair) - ipfs.files.add(arr, (err, res) => { - expect(err).to.not.exist - expect(res).to.be.length(1) - expect(res[0].path).to.equal('data.txt') - expect(res[0].node.size()).to.equal(17) - const mh = 'QmVv4Wz46JaZJeH5PMV4LGbRiiMKEmszPYY3g6fjGnVXBS' - expect(bs58.encode(res[0].node.multihash()).toString()).to.equal(mh) - done() + ipfs.files.add(arr, (err, res) => { + expect(err).to.not.exist + expect(res).to.be.length(1) + expect(res[0].path).to.equal('data.txt') + expect(res[0].node.size()).to.equal(17) + const mh = 'QmVv4Wz46JaZJeH5PMV4LGbRiiMKEmszPYY3g6fjGnVXBS' + expect(bs58.encode(res[0].node.multihash()).toString()).to.equal(mh) + done() + }) }) - }) - it('buffer as tuple', (done) => { - const file = { - path: 'testfile.txt', - content: smallFile - } + it('buffer as tuple', (done) => { + const file = { + path: 'testfile.txt', + content: smallFile + } - ipfs.files.add([file], (err, res) => { - expect(err).to.not.exist + ipfs.files.add([file], (err, res) => { + expect(err).to.not.exist - const added = res[0] != null ? res[0] : res - const mh = bs58.encode(added.node.multihash()).toString() - expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') - expect(added.path).to.equal('testfile.txt') - expect(added.node.links).to.have.length(0) - done() + const added = res[0] != null ? res[0] : res + const mh = bs58.encode(added.node.multihash()).toString() + expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') + expect(added.path).to.equal('testfile.txt') + expect(added.node.links).to.have.length(0) + done() + }) }) - }) - it('buffer', (done) => { - ipfs.files.add(smallFile, (err, res) => { - expect(err).to.not.exist + it('buffer', (done) => { + ipfs.files.add(smallFile, (err, res) => { + expect(err).to.not.exist - expect(res).to.have.length(1) - const mh = bs58.encode(res[0].node.multihash()).toString() - expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') - expect(res[0].path).to.equal(mh) - expect(res[0].node.links).to.have.length(0) - done() + expect(res).to.have.length(1) + const mh = bs58.encode(res[0].node.multihash()).toString() + expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') + expect(res[0].path).to.equal(mh) + expect(res[0].node.links).to.have.length(0) + done() + }) }) - }) - it('BIG buffer', (done) => { - ipfs.files.add(bigFile, (err, res) => { - expect(err).to.not.exist + it('BIG buffer', (done) => { + ipfs.files.add(bigFile, (err, res) => { + expect(err).to.not.exist - expect(res).to.have.length(1) - expect(res[0].node.links).to.have.length(58) - const mh = bs58.encode(res[0].node.multihash()).toString() - expect(res[0].path).to.equal(mh) - expect(mh).to.equal('Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq') - done() + expect(res).to.have.length(1) + expect(res[0].node.links).to.have.length(58) + const mh = bs58.encode(res[0].node.multihash()).toString() + expect(res[0].path).to.equal(mh) + expect(mh).to.equal('Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq') + done() + }) + }) + + it('add a nested dir as array', (done) => { + if (!isNode) { + return done() + // can't run this test cause browserify + // can't shim readFileSync in runtime + } + const base = path.join(__dirname, 'data/test-folder') + const content = (name) => ({ + path: `test-folder/${name}`, + content: fs.readFileSync(path.join(base, name)) + }) + const emptyDir = (name) => ({ + path: `test-folder/${name}` + }) + const dirs = [ + content('pp.txt'), + content('holmes.txt'), + content('jungle.txt'), + content('alice.txt'), + emptyDir('empty-folder'), + content('files/hello.txt'), + content('files/ipfs.txt'), + emptyDir('files/empty') + ] + + ipfs.files.add(dirs, (err, res) => { + expect(err).to.not.exist + + const added = res[res.length - 1] + const mh = bs58.encode(added.node.multihash()).toString() + expect(mh).to.equal('QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP') + expect(added.path).to.equal('test-folder') + expect(added.node.links).to.have.length(6) + done() + }) + }) + + describe('promise', () => { + it('buffer', () => { + return ipfs.files.add(smallFile) + .then((res) => { + const added = res[0] != null ? res[0] : res + const mh = bs58.encode(added.node.multihash()).toString() + expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') + expect(added.path).to.equal(mh) + expect(added.node.links).to.have.length(0) + }) + .catch((err) => { + expect(err).to.not.exist + }) + }) }) }) - it('add a nested dir as array', (done) => { - if (!isNode) { - return done() - // can't run this test cause browserify - // can't shim readFileSync in runtime - } - const base = path.join(__dirname, 'data/test-folder') - const content = (name) => ({ - path: `test-folder/${name}`, - content: fs.readFileSync(path.join(base, name)) + describe('.cat', () => { + it('returns file stream', (done) => { + const hash = 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB' + ipfs.cat(hash, (err, file) => { + expect(err).to.not.exist + file.pipe(bl((err, bldata) => { + expect(err).to.not.exist + expect(bldata.toString()).to.contain('Check out some of the other files in this directory:') + done() + })) + }) }) - const emptyDir = (name) => ({ - path: `test-folder/${name}` + + // This fails on js-ipfs-api + it('takes a buffer input', (done) => { + const mhBuf = new Buffer(bs58.decode('QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB')) + ipfs.cat(mhBuf, (err, file) => { + expect(err).to.not.exist + file.pipe(bl((err, bldata) => { + expect(err).to.not.exist + expect(bldata.toString()).to.contain('Check out some of the other files in this directory:') + done() + })) + }) }) - const dirs = [ - content('pp.txt'), - content('holmes.txt'), - content('jungle.txt'), - content('alice.txt'), - emptyDir('empty-folder'), - content('files/hello.txt'), - content('files/ipfs.txt'), - emptyDir('files/empty') - ] - - ipfs.files.add(dirs, (err, res) => { - expect(err).to.not.exist - const added = res[res.length - 1] - const mh = bs58.encode(added.node.multihash()).toString() - expect(mh).to.equal('QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP') - expect(added.path).to.equal('test-folder') - expect(added.node.links).to.have.length(6) - done() + // You can add a large file to your ipfs repo and change the hash to the file after installing js-ipfs + it('returns a large file', (done) => { + const hash = 'Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq' + ipfs.cat(hash, (err, file) => { + expect(err).to.not.exist + file.pipe(bl((err, bldata) => { + expect(err).to.not.exist + expect(bldata).to.deep.equal(bigFile) + done() + })) + }) }) - }) - describe('promise', () => { - it('buffer', () => { - return ipfs.files.add(smallFile) - .then((res) => { - const added = res[0] != null ? res[0] : res - const mh = bs58.encode(added.node.multihash()).toString() - expect(mh).to.equal('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') - expect(added.path).to.equal(mh) - expect(added.node.links).to.have.length(0) + it('returns error on invalid key', (done) => { + const hash = 'somethingNotMultihash' + ipfs.cat(hash, (err, file) => { + expect(err).to.exist + const errString = err.toString() + if (errString === 'Error: invalid ipfs ref path') { + expect(err.toString()).to.contain('Error: invalid ipfs ref path') + } + if (errString === 'Error: Invalid Key') { + expect(err.toString()).to.contain('Error: Invalid Key') + } + done() + }) + }) + + describe('promise', () => { + it('files.cat', (done) => { + const hash = 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB' + ipfs.cat(hash) + .then((stream) => { + stream.pipe(bl((err, bldata) => { + expect(err).to.not.exist + expect(bldata.toString()).to.contain('Check out some of the other files in this directory:') + done() + })) + }) + .catch((err) => { + expect(err).to.not.exist + }) + }) + + it('returns error on invalid key', (done) => { + const hash = 'somethingNotMultihash' + ipfs.cat(hash) + .then((stream) => {}) + .catch((err) => { + expect(err).to.exist + const errString = err.toString() + if (errString === 'Error: invalid ipfs ref path') { + expect(err.toString()).to.contain('Error: invalid ipfs ref path') + } + if (errString === 'Error: Invalid Key') { + expect(err.toString()).to.contain('Error: Invalid Key') + } + done() + }) + }) + + it('takes a buffer input', (done) => { + const hash = new Buffer(bs58.decode('QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB')) + ipfs.cat(hash) + .then((stream) => { + stream.pipe(bl((err, bldata) => { + expect(err).to.not.exist + expect(bldata.toString()).to.contain('Check out some of the other files in this directory:') + done() + })) }) .catch((err) => { expect(err).to.not.exist }) + }) }) }) })