Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Ipfs file upload progress #604

Merged
merged 12 commits into from
Oct 18, 2017
2 changes: 1 addition & 1 deletion examples/upload-file-via-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"devDependencies": {
"babel-core": "^5.4.7",
"babel-loader": "^5.1.2",
"ipfs-api": "^12.1.7",
"ipfs-api": "../../",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an example in the examples directory. Just repointed it to use the current ipfs-api rather than a published version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, now it makes sense 👍

"json-loader": "^0.5.4",
"react": "^15.4.2",
"react-dom": "^15.4.2",
Expand Down
2 changes: 1 addition & 1 deletion examples/upload-file-via-browser/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class App extends React.Component {
saveToIpfs (reader) {
let ipfsId
const buffer = Buffer.from(reader.result)
this.ipfsApi.add(buffer)
this.ipfsApi.add(buffer, { progress: (prog) => console.log(`received: ${prog}`) })
.then((response) => {
console.log(response)
ipfsId = response[0].hash
Expand Down
6 changes: 4 additions & 2 deletions src/files/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const isStream = require('is-stream')
const promisify = require('promisify-es6')
const DAGNodeStream = require('../utils/dagnode-stream')
const ProgressStream = require('../utils/progress-stream')

module.exports = (send) => {
return promisify((files, opts, callback) => {
Expand Down Expand Up @@ -41,10 +42,11 @@ module.exports = (send) => {
qs.hash = opts.hashAlg
}

const request = { path: 'add', files: files, qs: qs }
const request = { path: 'add', files: files, qs: qs, progress: opts.progress }

// Transform the response stream to DAGNode values
const transform = (res, callback) => DAGNodeStream.streamToValue(send, res, callback)
const transform = (res, callback) => DAGNodeStream
.streamToValue(send, ProgressStream.fromStream(opts.progress, res), callback)
send.andTransform(request, transform, callback)
})
}
49 changes: 49 additions & 0 deletions src/utils/progress-stream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

const Transform = require('readable-stream').Transform

/*
A transform stream to track progress events on file upload

When the progress flag is passed to the HTTP api, the stream
emits progress events like such:

{
Name string
Hash string `json:",omitempty"`
Bytes int64 `json:",omitempty"`
Size string `json:",omitempty"`
}

This class will take care of detecting such
events and calling the associated track method
with the bytes sent so far as parameter. It will
also skip them from the stream, emitting only
when the final object has been uploaded and we
got a hash.
*/
class ProgressStream extends Transform {
constructor (opts) {
opts = Object.assign(opts || {}, { objectMode: true })
super(opts)
this._track = opts.track || (() => {})
}

static fromStream (track, stream) {
const prog = new ProgressStream({ track })
return stream.pipe(prog)
}

_transform (chunk, encoding, callback) {
if (chunk &&
typeof chunk.Bytes !== 'undefined' &&
typeof chunk.Hash === 'undefined') {
this._track(chunk.Bytes)
return callback()
}

callback(null, chunk)
}
}

module.exports = ProgressStream
3 changes: 3 additions & 0 deletions src/utils/request-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ function requestAPI (config, options, callback) {
if (options.files && !Array.isArray(options.files)) {
options.files = [options.files]
}
if (options.progress) {
options.qs.progress = true
}

if (options.qs.r) {
options.qs.recursive = options.qs.r
Expand Down
39 changes: 39 additions & 0 deletions test/files.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,45 @@ describe('.files (the MFS API part)', function () {
})
})

it('files.add with progress options', (done) => {
let progress = 0
ipfs.files.add(testfile, { progress: (p) => { progress = p } }, (err, res) => {
expect(err).to.not.exist()

expect(res).to.have.length(1)
expect(progress).to.be.greaterThan(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should exactly be 100 right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed with @victorbjelkholm

done()
})
})

it('files.add without progress options', (done) => {
ipfs.files.add(testfile, (err, res) => {
expect(err).to.not.exist()

expect(res).to.have.length(1)
done()
})
})

HASH_ALGS.forEach((name) => {
it(`files.add with hash=${name} and raw-leaves=false`, (done) => {
const content = String(Math.random() + Date.now())
const file = {
path: content + '.txt',
content: Buffer.from(content)
}
const options = { hash: name, 'raw-leaves': false }

ipfs.files.add([file], options, (err, res) => {
if (err) return done(err)
expect(res).to.have.length(1)
const cid = new CID(res[0].hash)
expect(mh.decode(cid.multihash).name).to.equal(name)
done()
})
})
})

it('files.mkdir', (done) => {
ipfs.files.mkdir('/test-folder', done)
})
Expand Down