Skip to content

Commit 2675fe5

Browse files
author
Alan Shaw
committed
feat: add MFS read and write
License: MIT Signed-off-by: Alan Shaw <[email protected]>
1 parent 2a52d27 commit 2675fe5

20 files changed

+865
-465
lines changed

API.md

+78-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
* [`cat`](#cat)
99
* [`get`](#get)
1010
* [`id`](#id)
11+
* [`read`](#read)
1112
* [`start`](#start)
1213
* [`stop`](#stop)
1314
* [`version`](#version)
15+
* [`write`](#write)
1416

1517
## Getting started
1618

@@ -219,7 +221,7 @@ Get file contents.
219221
| Name | Type | Description |
220222
|------|------|-------------|
221223
| path | `String`\|`Buffer`\|[`CID`](https://www.npmjs.com/package/cids) | IPFS path or CID to cat data from |
222-
| options | `Object` | (optional) options |
224+
| options | `Object` | (optional) options TODO |
223225

224226
#### Returns
225227

@@ -329,6 +331,36 @@ console.log(info)
329331
*/
330332
```
331333

334+
## read
335+
336+
Read data from an MFS (Mutable File System) path.
337+
338+
### `node.read(path, [options])`
339+
340+
#### Parameters
341+
342+
| Name | Type | Description |
343+
|------|------|-------------|
344+
| path | `String` | MFS path of the _file_ to read (not a directory) |
345+
| options | `Object` | (optional) options |
346+
| options.offset | `Number` | Byte offset to begin reading at, default: `0` |
347+
| options.length | `Number` | Number of bytes to read, default: `undefined` (read all bytes) |
348+
349+
#### Returns
350+
351+
| Type | Description |
352+
|------|-------------|
353+
| `Iterator<Buffer>` | An iterator that can be used to consume all the data |
354+
355+
#### Example
356+
357+
```js
358+
let data = Buffer.alloc(0)
359+
for (const chunk of node.read('/path/to/mfs/file')) {
360+
data = Buffer.concat([data, chunk])
361+
}
362+
```
363+
332364
## start
333365

334366
Start the IPFS node.
@@ -386,3 +418,48 @@ const info = await node.version()
386418
console.log(info)
387419
// { version: '0.32.2', repo: 7, commit: '' }
388420
```
421+
422+
## write
423+
424+
Write data to an MFS (Mutable File System) file.
425+
426+
### `node.write(path, input, [options])`
427+
428+
#### Parameters
429+
430+
| Name | Type | Description |
431+
|------|------|-------------|
432+
| path | `String` | MFS path of the _file_ to write to |
433+
| input | `Buffer`\|`String`\|`Iterable`\|`Iterator` | Input data |
434+
| options | `Object` | (optional) options |
435+
| options.offset | `Number` | Byte offset to begin writing at, default: `0` |
436+
| options.length | `Number` | Number of bytes to write, default: `undefined` (write all bytes) |
437+
| options.create | `Boolean` | Create file if not exists, default: `true` |
438+
| options.parents | `Boolean` | Create parent directories if not exists, default: `false` |
439+
| options.truncate | `Boolean` | Truncate the file after writing, default: `false` |
440+
| options.rawLeaves | `Boolean` | Do not wrap leaf nodes in a protobuf, default: `false` |
441+
| options.cidVersion | `Number` | CID version to use when creating the node(s), default: 1 |
442+
443+
#### Returns
444+
445+
| Type | Description |
446+
|------|-------------|
447+
| `Promise` | Resolved when the write has been completed |
448+
449+
#### Example
450+
451+
Write a buffer/string:
452+
453+
```js
454+
await node.write('/hello-world.txt', Buffer.from('hello world!'))
455+
```
456+
457+
```js
458+
await node.write('/example/hello-world.txt', 'hello world!', { parents: true })
459+
```
460+
461+
Write a Node.js stream:
462+
463+
```js
464+
await node.write('/example.js', fs.createReadStream(__filename), { create: true })
465+
```

RATIONALE.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,20 @@ Are the same options but they have been renamed to be less cryptic.
5757

5858
The default value for CID codec is changed from `dag-pb` to `raw`. This is to encourage users of the API to add correctly formed data.
5959

60-
### CID version
60+
### CID version 1
6161

6262
Changed from 0 to 1. This will soon be the default for all new content added to IPFS anyway.
6363

6464
### Iterator input
6565

6666
This makes adding multiple blocks significantly easier and allows us to easily integrate with [`js-unixfsv2-draft`](https://github.com/mikeal/js-unixfsv2-draft).
67+
68+
## `write`
69+
70+
### Create by default
71+
72+
Similar to Node.js `fs.createWriteStream` or `fs.writeFile`, 99.9% of the time we want to create the file if it doesn't exist, why make this difficult?
73+
74+
### CID version 1
75+
76+
Changed from 0 to 1. This will soon be the default for all new content added to IPFS anyway.

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const node = await ipfsx(new IPFS)
6161
// Add something to IPFS
6262
const { cid } = await node.add('hello world').first()
6363

64-
// Stream content from IPFs using async iterators
64+
// Stream content from IPFS using async iterators
6565
let data = Buffer.alloc(0)
6666
for await (const chunk of node.cat(cid)) {
6767
data = Buffer.concat([data, chunk])
@@ -80,9 +80,11 @@ for await (const chunk of node.cat(cid)) {
8080
* [`cat`](https://github.com/alanshaw/ipfsx/blob/master/API.md#cat)
8181
* [`get`](https://github.com/alanshaw/ipfsx/blob/master/API.md#get)
8282
* [`id`](https://github.com/alanshaw/ipfsx/blob/master/API.md#id)
83+
* [`read`](https://github.com/alanshaw/ipfsx/blob/master/API.md#read)
8384
* [`start`](https://github.com/alanshaw/ipfsx/blob/master/API.md#start)
8485
* [`stop`](https://github.com/alanshaw/ipfsx/blob/master/API.md#stop)
8586
* [`version`](https://github.com/alanshaw/ipfsx/blob/master/API.md#version)
87+
* [`write`](https://github.com/alanshaw/ipfsx/blob/master/API.md#write)
8688
* TODO: more to come in upcoming releases!
8789

8890
## Contribute

examples/add/nodejs-stream.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
const Os = require('os')
3+
const Path = require('path')
4+
const Fs = require('fs')
5+
const Ipfs = require('ipfs')
6+
const ipfsx = require('../../')
7+
8+
async function main () {
9+
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
10+
11+
const { cid } = await node.add(Fs.createReadStream(__filename)).first()
12+
13+
let buffer = Buffer.alloc(0)
14+
for await (const chunk of node.cat(cid)) {
15+
buffer = Buffer.concat([buffer, chunk])
16+
}
17+
18+
if (!buffer.equals(await Fs.promises.readFile(__filename))) {
19+
throw new Error('something went wrong, data written was not equal to data read')
20+
}
21+
22+
console.log('success! data written was equal to data read')
23+
await node.stop()
24+
}
25+
26+
main()

examples/block/stat.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ const Path = require('path')
33
const Ipfs = require('ipfs')
44
const ipfsx = require('../../')
55

6-
;(async () => {
6+
async function main () {
77
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
88
const data = Buffer.from('hello world')
99
const block = await node.block.put(data).first()
1010
const stats = await node.block.stat(block.cid)
1111

1212
console.log(block.cid.toBaseEncodedString(), stats)
1313
// zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn { size: 11 }
14-
})()
14+
15+
await node.stop()
16+
}
17+
18+
main()

examples/get/directory.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const ipfsx = require('../../')
66

77
const rootDir = Path.resolve(__dirname, '..', '..')
88

9-
;(async () => {
9+
async function main () {
1010
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
1111

1212
const { cid, path } = await node.add([
@@ -30,4 +30,8 @@ const rootDir = Path.resolve(__dirname, '..', '..')
3030

3131
console.log('Data:', data)
3232
}
33-
})()
33+
34+
await node.stop()
35+
}
36+
37+
main()

examples/id.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,22 @@ const Path = require('path')
33
const Ipfs = require('ipfs')
44
const ipfsx = require('../')
55

6-
;(async () => {
6+
async function main () {
77
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
88
const info = await node.id()
99
console.log(info)
10-
})()
10+
/*
11+
{ id: 'QmTq6pfziFuG2Twi5FnVQwua5mNtSXbEXuExjYy6XZd5Qt',
12+
publicKey:
13+
'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCEgPFgwTrphILHGQo0AErm/F3Awwl6Pxa9TTlkJShEu0swQq6ENTcT34YmiJ/DiC+a+HmbUeisi0B+ebzhW3MnZSXWb5mCQC4yz22MChedWYSieQq0/jFGRezhAZnZ7PYFw0MEEHLaXHDuJRF3NvrddcFxBWBlk0mu9FMv75c/QUN5aqnnZ7fiVpKqO78W5xKXcOyCdxCAb1ul0YZWUp4HjIcn4g+Fw6lQEh/kLjubTB3rKH41dcOPhv41nUKSQIqAaDeakFQfADEz86+mUaW2xTL931mtWWeBLI36KaLNG5uSnGMhqykxqy9/9LzVTEMa8r6U7kMcfRl2m+xCVWlRAgMBAAE=',
14+
addresses:
15+
[ '/ip4/127.0.0.1/tcp/4002/ipfs/QmTq6pfziFuG2Twi5FnVQwua5mNtSXbEXuExjYy6XZd5Qt',
16+
'/ip4/127.0.0.1/tcp/4003/ws/ipfs/QmTq6pfziFuG2Twi5FnVQwua5mNtSXbEXuExjYy6XZd5Qt',
17+
'/ip4/192.168.0.17/tcp/4002/ipfs/QmTq6pfziFuG2Twi5FnVQwua5mNtSXbEXuExjYy6XZd5Qt' ],
18+
agentVersion: 'js-ipfs/0.32.2',
19+
protocolVersion: '9000' }
20+
*/
21+
await node.stop()
22+
}
23+
24+
main()

examples/read+write/iterator.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
const Os = require('os')
3+
const Path = require('path')
4+
const Ipfs = require('ipfs')
5+
const { randomBytes } = require('crypto')
6+
const { inspect } = require('util')
7+
const ipfsx = require('../../')
8+
9+
async function main () {
10+
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
11+
12+
const path = `/example-read+write-iterator-${Date.now()}.txt`
13+
const { data, iterator } = randomData()
14+
15+
console.log('iterator[Symbol.iterable]=', iterator)
16+
17+
console.log(`writing to ${path}`)
18+
await node.write(path, iterator)
19+
20+
console.log(`reading from ${path}`)
21+
let buffer = Buffer.alloc(0)
22+
for await (const chunk of node.read(path)) {
23+
console.log(`read ${inspect(chunk)}`)
24+
buffer = Buffer.concat([buffer, chunk])
25+
}
26+
27+
if (!buffer.equals(data)) {
28+
throw new Error('something went wrong, data written was not equal to data read')
29+
}
30+
31+
console.log('success! data written was equal to data read')
32+
await node.stop()
33+
}
34+
35+
function randomData () {
36+
const data = Array.from(Array(100000).fill(0), () => randomBytes(16))
37+
const it = function * () {
38+
for (var i = 0; i < data.length; i++) {
39+
console.log(`writing ${inspect(data[i])}`)
40+
yield data[i]
41+
}
42+
}
43+
console.log(it)
44+
return {
45+
data: Buffer.concat(data),
46+
iterator: (it)()
47+
}
48+
}
49+
50+
main()

examples/read+write/nodejs-stream.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
const Os = require('os')
3+
const Path = require('path')
4+
const Fs = require('fs')
5+
const Ipfs = require('ipfs')
6+
const ipfsx = require('../../')
7+
8+
async function main () {
9+
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
10+
11+
const path = `/example-read+write-nodejs-stream-${Date.now()}.js`
12+
13+
await node.write(path, Fs.createReadStream(__filename))
14+
15+
let buffer = Buffer.alloc(0)
16+
for await (const chunk of node.read(path)) {
17+
buffer = Buffer.concat([buffer, chunk])
18+
}
19+
20+
if (!buffer.equals(await Fs.promises.readFile(__filename))) {
21+
throw new Error('something went wrong, data written was not equal to data read')
22+
}
23+
24+
console.log('success! data written was equal to data read')
25+
await node.stop()
26+
}
27+
28+
main()

examples/read+write/string.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
const Os = require('os')
3+
const Path = require('path')
4+
const Ipfs = require('ipfs')
5+
const ipfsx = require('../../')
6+
7+
async function main () {
8+
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
9+
10+
const now = new Date()
11+
const path = `/example-read+write-string-${now.getTime()}.txt`
12+
const data = `The date is now ${now.toISOString()}`
13+
14+
console.log(`writing "${data}" to ${path}`)
15+
await node.write(path, data)
16+
17+
console.log(`reading from ${path}`)
18+
let buffer = Buffer.alloc(0)
19+
for await (const chunk of node.read(path)) {
20+
buffer = Buffer.concat([buffer, chunk])
21+
}
22+
23+
if (buffer.toString() !== data) {
24+
throw new Error('something went wrong, data written was not equal to data read')
25+
}
26+
27+
console.log('success! data written was equal to data read')
28+
await node.stop()
29+
}
30+
31+
main()

examples/version.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ const Path = require('path')
33
const Ipfs = require('ipfs')
44
const ipfsx = require('../')
55

6-
;(async () => {
6+
async function main () {
77
const node = await ipfsx(new Ipfs({ repo: Path.join(Os.tmpdir(), `${Date.now()}`) }))
88
const info = await node.version()
99
console.log(info)
1010
// { version: '0.32.2', repo: 7, commit: '' }
11-
})()
11+
await node.stop()
12+
}
13+
14+
main()

0 commit comments

Comments
 (0)