Skip to content

Commit

Permalink
feat!: automatic client side CAR chunking for large data (#588)
Browse files Browse the repository at this point in the history
`storeBlob`, `storeDirectory` and `store` now construct CAR files and then call `storeCar` (which `POST`s to `/upload`). This automatically gives them CAR chunking capability.

It adds the following **static** functions:

```ts
NFTStorage.encodeBlob(blob: Blob): Promise<{ cid: CID, car: CarReader }>

NFTStorage.encodeDirectory(files: Blob[]): Promise<{ cid: CID, car: CarReader }>

NFTStorage.encodeNFT<T extends TokenInput>(input: T): Promise<{ cid: CID, token: Token<T>, car: CarReader }>
```

After encoding your CAR and obtaining the root CID you can call:

```js
await client.storeCar(car)
```

🚨 There are trade offs here:

1. We're always sending CAR files so our `type` field is going to always be `Car` for uploads from the JS client.

    To mitigate this we could simply inspect the root node of the CAR, from this we can assertain the type of the data. We should talk about our `type` field and what it means. Right now we've mapped it to the method of upload...lets resolve this and submit a separate PR to fix.

    FYI, I'm going to be implementing [#355](#355) soon so will be inspecting the root node of the CAR anyway for validation purposes.

2. The `files` property is not set for the `Multipart` or `Nft` type uploads (since it's being uploaded as a CAR).

    I actually can't see any requests to the `/status/:cid` API (literally 0 - everyone uses `/check/:cid`) in the cloudflare logs which is the only place this data is exposed to users. I don't think folks will miss it. However if we inspect the root node we can get a shallow directory listing to put here.

    For `Nft` types we can't really set it anymore. There's also a regression right now where we're _not_ setting it anyway. As an aside I'm not sure how much value it has since the data is just a list of file names, without paths within the object that is created...

resolves #220
  • Loading branch information
Alan Shaw authored Nov 16, 2021
1 parent 5deb30b commit 437ae4f
Show file tree
Hide file tree
Showing 15 changed files with 975 additions and 551 deletions.
14 changes: 7 additions & 7 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,30 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
"@web-std/blob": "^2.1.0",
"@ipld/car": "^3.1.20",
"@web-std/blob": "^2.1.2",
"@web-std/fetch": "^2.0.3",
"@web-std/file": "^1.1.0",
"@web-std/form-data": "^2.1.0",
"@web-std/file": "^1.1.3",
"@web-std/form-data": "^2.1.1",
"carbites": "^1.0.6",
"ipfs-car": "^0.5.9",
"multiformats": "^9.4.10",
"p-retry": "^4.6.1",
"streaming-iterables": "^6.0.0"
},
"devDependencies": {
"@ipld/car": "^3.1.20",
"@ipld/dag-cbor": "^6.0.13",
"@ipld/dag-json": "^8.0.3",
"@ssttevee/multipart-parser": "0.1.9",
"@types/mocha": "^9.0.0",
"hundreds": "0.0.9",
"ipfs-car": "^0.5.6",
"ipfs-unixfs-importer": "9.0.6",
"ipfs-unixfs-importer": "^9.0.6",
"ipld": "0.30.2",
"ipld-dag-pb": "0.22.3",
"ipld-garbage": "^4.0.1",
"ipld-in-memory": "8.0.0",
"mocha": "^9.1.0",
"multicodec": "3.2.1",
"multicodec": "^3.2.1",
"multihashing-async": "^2.1.2",
"npm-run-all": "^4.1.5",
"nyc": "15.1.0",
Expand Down
65 changes: 65 additions & 0 deletions packages/client/src/bs-car-reader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* An implementation of the CAR reader interface that is backed by a blockstore.
*
* @typedef {import('multiformats').CID} CID
* @typedef {import('@ipld/car/api').CarReader} CarReader
* @implements {CarReader}
*/
export class BlockstoreCarReader {
/**
* @param {number} version
* @param {CID[]} roots
* @param {import('ipfs-car/blockstore').Blockstore} blockstore
*/
constructor(version, roots, blockstore) {
/**
* @private
*/
this._version = version
/**
* @private
*/
this._roots = roots
/**
* @private
*/
this._blockstore = blockstore
}

get version() {
return this._version
}

get blockstore() {
return this._blockstore
}

async getRoots() {
return this._roots
}

/**
* @param {CID} cid
*/
has(cid) {
return this._blockstore.has(cid)
}

/**
* @param {CID} cid
*/
async get(cid) {
const bytes = await this._blockstore.get(cid)
return { cid, bytes }
}

blocks() {
return this._blockstore.blocks()
}

async *cids() {
for await (const b of this.blocks()) {
yield b.cid
}
}
}
Loading

0 comments on commit 437ae4f

Please sign in to comment.