Skip to content

Commit 7b49f7e

Browse files
hugomrdiasAlan Shaw
authored and
Alan Shaw
committed
feat: add support for File DOM API to files-regular (ipfs#986)
* feat: add file dom api support to files api * feat: add support for File DOM API to files-regular * chore: fix package declaration cause npm is dumb * chore: fix lint * chore: add ipfs-utils * fix: change the requires to ipfs-utils * chore: increase max bundle size
1 parent 8a4062a commit 7b49f7e

File tree

11 files changed

+96
-92
lines changed

11 files changed

+96
-92
lines changed

.aegir.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const createServer = require('ipfsd-ctl').createServer
55
const server = createServer()
66

77
module.exports = {
8-
bundlesize: { maxSize: '231kB' },
8+
bundlesize: { maxSize: '232kB' },
99
webpack: {
1010
resolve: {
1111
mainFields: ['browser', 'main']

examples/upload-file-via-browser/.eslintrc

-11
This file was deleted.

examples/upload-file-via-browser/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules
22
npm-debug.log
33
.DS_Store
44
dist
5+
yarn.lock

examples/upload-file-via-browser/package.json

+19-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.0.0",
44
"description": "Upload file to IPFS via browser using js-ipfs-http-client with Webpack",
55
"scripts": {
6-
"start": "node server.js"
6+
"start": "webpack-dev-server"
77
},
88
"author": "Harlan T Wood <code@harlantwood.net>",
99
"contributors": [
@@ -12,12 +12,24 @@
1212
"license": "MIT",
1313
"devDependencies": {
1414
"@babel/core": "^7.4.3",
15-
"ipfs-http-client": "../../",
16-
"pull-file-reader": "~1.0.2",
17-
"react": "~16.8.6",
18-
"react-dom": "~16.8.6",
19-
"react-hot-loader": "~4.8.4",
20-
"webpack": "~4.31.0",
15+
"@babel/preset-env": "^7.3.1",
16+
"@babel/preset-react": "^7.0.0",
17+
"eslint": "^5.16.0",
18+
"eslint-plugin-react": "^7.11.1",
19+
"react": "~16.6.3",
20+
"react-dom": "~16.6.3",
21+
"webpack": "~4.30.0",
2122
"webpack-dev-server": "~3.3.1"
23+
},
24+
"eslintConfig" : {
25+
"extends": "standard",
26+
"rules": {
27+
"react/jsx-uses-react": 2,
28+
"react/jsx-uses-vars": 2,
29+
"react/react-in-jsx-scope": 2
30+
},
31+
"plugins": [
32+
"react"
33+
]
2234
}
2335
}

examples/upload-file-via-browser/server.js

-13
This file was deleted.

examples/upload-file-via-browser/src/App.js

+11-16
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
'use strict'
22
const React = require('react')
3-
const ipfsClient = require('ipfs-http-client')
4-
5-
// create a stream from a file, which enables uploads of big files without allocating memory twice
6-
const fileReaderPullStream = require('pull-file-reader')
3+
const ipfsClient = require('../../../src')
74

85
class App extends React.Component {
96
constructor () {
107
super()
118
this.state = {
129
added_file_hash: null
1310
}
14-
this.ipfs = ipfsClient('localhost', '5001')
11+
this.ipfs = ipfsClient('localhost', '58041')
1512

1613
// bind methods
1714
this.captureFile = this.captureFile.bind(this)
@@ -22,38 +19,36 @@ class App extends React.Component {
2219
captureFile (event) {
2320
event.stopPropagation()
2421
event.preventDefault()
25-
const file = event.target.files[0]
2622
if (document.getElementById('keepFilename').checked) {
27-
this.saveToIpfsWithFilename(file)
23+
this.saveToIpfsWithFilename(event.target.files)
2824
} else {
29-
this.saveToIpfs(file)
25+
this.saveToIpfs(event.target.files)
3026
}
3127
}
3228

3329
// Example #1
3430
// Add file to IPFS and return a CID
35-
saveToIpfs (file) {
31+
saveToIpfs (files) {
3632
let ipfsId
37-
const fileStream = fileReaderPullStream(file)
38-
this.ipfs.add(fileStream, { progress: (prog) => console.log(`received: ${prog}`) })
33+
this.ipfs.add([...files], { progress: (prog) => console.log(`received: ${prog}`) })
3934
.then((response) => {
4035
console.log(response)
4136
ipfsId = response[0].hash
4237
console.log(ipfsId)
43-
this.setState({added_file_hash: ipfsId})
38+
this.setState({ added_file_hash: ipfsId })
4439
}).catch((err) => {
4540
console.error(err)
4641
})
4742
}
4843

4944
// Example #2
5045
// Add file to IPFS and wrap it in a directory to keep the original filename
51-
saveToIpfsWithFilename (file) {
46+
saveToIpfsWithFilename (files) {
47+
const file = [...files][0]
5248
let ipfsId
53-
const fileStream = fileReaderPullStream(file)
5449
const fileDetails = {
5550
path: file.name,
56-
content: fileStream
51+
content: file
5752
}
5853
const options = {
5954
wrapWithDirectory: true,
@@ -65,7 +60,7 @@ class App extends React.Component {
6560
// CID of wrapping directory is returned last
6661
ipfsId = response[response.length - 1].hash
6762
console.log(ipfsId)
68-
this.setState({added_file_hash: ipfsId})
63+
this.setState({ added_file_hash: ipfsId })
6964
}).catch((err) => {
7065
console.error(err)
7166
})

examples/upload-file-via-browser/webpack.config.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
'use strict'
22

33
const path = require('path')
4-
const webpack = require('webpack')
54

65
module.exports = {
76
mode: 'development',
87
devtool: 'eval',
98
entry: [
10-
'webpack-dev-server/client?http://localhost:3000',
11-
'webpack/hot/only-dev-server',
129
'./src/index'
1310
],
1411
output: {
1512
path: path.join(__dirname, 'dist'),
1613
filename: 'bundle.js',
1714
publicPath: '/static/'
1815
},
19-
plugins: [
20-
new webpack.HotModuleReplacementPlugin()
21-
],
2216
module: {
2317
rules: [
2418
{
@@ -28,8 +22,7 @@ module.exports = {
2822
{
2923
loader: 'babel-loader',
3024
options: {
31-
presets: ['@babel/preset-env', '@babel/preset-react'],
32-
plugins: ['react-hot-loader/babel']
25+
presets: ['@babel/preset-env', '@babel/preset-react']
3326
}
3427
}
3528
]

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@
4747
"is-stream": "^2.0.0",
4848
"iso-stream-http": "~0.1.2",
4949
"iso-url": "~0.4.6",
50+
"ipfs-utils": "~0.0.3",
5051
"just-kebab-case": "^1.1.0",
5152
"just-map-keys": "^1.1.0",
53+
"kind-of": "^6.0.2",
5254
"lru-cache": "^5.1.1",
5355
"multiaddr": "^6.0.6",
5456
"multibase": "~0.6.0",
@@ -83,9 +85,8 @@
8385
"chai": "^4.2.0",
8486
"cross-env": "^5.2.0",
8587
"dirty-chai": "^2.0.1",
86-
"eslint-plugin-react": "^7.11.1",
8788
"go-ipfs-dep": "0.4.19",
88-
"interface-ipfs-core": "~0.101.1",
89+
"interface-ipfs-core": "~0.102.0",
8990
"ipfsd-ctl": "~0.42.0",
9091
"nock": "^10.0.2",
9192
"stream-equal": "^1.1.1"

src/files-regular/add.js

+6-20
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
const promisify = require('promisify-es6')
44
const ConcatStream = require('concat-stream')
55
const once = require('once')
6-
const isStream = require('is-stream')
7-
const isSource = require('is-pull-stream').isSource
6+
const { isSource } = require('is-pull-stream')
87
const FileResultStreamConverter = require('../utils/file-result-stream-converter')
98
const SendFilesStream = require('../utils/send-files-stream')
9+
const validateAddInput = require('ipfs-utils/src/files/add-input-validation')
1010

1111
module.exports = (send) => {
1212
const createAddStream = SendFilesStream(send, 'add')
@@ -16,31 +16,17 @@ module.exports = (send) => {
1616
_callback = options
1717
options = null
1818
}
19-
2019
const callback = once(_callback)
2120

2221
if (!options) {
2322
options = {}
2423
}
2524
options.converter = FileResultStreamConverter
2625

27-
// Buffer, pull stream or Node.js stream
28-
const isBufferOrStream = obj => Buffer.isBuffer(obj) || isStream.readable(obj) || isSource(obj)
29-
// An object like { content?, path? }, where content isBufferOrStream and path isString
30-
const isContentObject = obj => {
31-
if (typeof obj !== 'object') return false
32-
// path is optional if content is present
33-
if (obj.content) return isBufferOrStream(obj.content)
34-
// path must be a non-empty string if no content
35-
return Boolean(obj.path) && typeof obj.path === 'string'
36-
}
37-
// An input atom: a buffer, stream or content object
38-
const isInput = obj => isBufferOrStream(obj) || isContentObject(obj)
39-
// All is ok if data isInput or data is an array of isInput
40-
const ok = isInput(_files) || (Array.isArray(_files) && _files.every(isInput))
41-
42-
if (!ok) {
43-
return callback(new Error('invalid input: expected buffer, readable stream, pull stream, object or array of objects'))
26+
try {
27+
validateAddInput(_files)
28+
} catch (err) {
29+
return callback(err)
4430
}
4531

4632
const files = [].concat(_files)

src/utils/multipart.js

-7
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
const Transform = require('readable-stream').Transform
44
const isNode = require('detect-node')
5-
const isSource = require('is-pull-stream').isSource
6-
const toStream = require('pull-to-stream')
75

86
const PADDING = '--'
97
const NEW_LINE = '\r\n'
@@ -77,12 +75,7 @@ class Multipart extends Transform {
7775
return callback() // early
7876
}
7977

80-
if (isSource(content)) {
81-
content = toStream.readable(content)
82-
}
83-
8478
// From now on we assume content is a stream
85-
8679
content.once('error', this.emit.bind(this, 'error'))
8780

8881
content.once('end', () => {

src/utils/prepare-file.js

+54-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
const isNode = require('detect-node')
44
const flatmap = require('flatmap')
5+
const { Readable } = require('readable-stream')
6+
const kindOf = require('kind-of')
7+
const { isSource } = require('is-pull-stream')
8+
const isStream = require('is-stream')
9+
const pullToStream = require('pull-to-stream')
10+
const { supportsFileReader } = require('ipfs-utils/src/supports')
11+
const streamFromFileReader = require('ipfs-utils/src/streams/stream-from-filereader')
512

613
function loadPaths (opts, file) {
714
const path = require('path')
@@ -73,10 +80,36 @@ function loadPaths (opts, file) {
7380
}
7481
}
7582

83+
function contentToStream (content) {
84+
if (supportsFileReader && kindOf(content) === 'file') {
85+
return streamFromFileReader(content)
86+
}
87+
88+
if (kindOf(content) === 'buffer') {
89+
return new Readable({
90+
read () {
91+
this.push(content)
92+
this.push(null)
93+
}
94+
})
95+
}
96+
97+
if (isSource(content)) {
98+
return pullToStream.readable(content)
99+
}
100+
101+
if (isStream.readable(content)) {
102+
return content
103+
}
104+
105+
throw new Error(`Input not supported. Expected Buffer|ReadableStream|PullStream|File got ${kindOf(content)}. Check the documentation for more info https://github.com/ipfs/interface-js-ipfs-core/blob/master/SPEC/FILES.md#add`)
106+
}
107+
76108
function prepareFile (file, opts) {
77109
let files = [].concat(file)
78110

79111
return flatmap(files, (file) => {
112+
// add from fs with file path
80113
if (typeof file === 'string') {
81114
if (!isNode) {
82115
throw new Error('Can only add file paths in node')
@@ -85,20 +118,34 @@ function prepareFile (file, opts) {
85118
return loadPaths(opts, file)
86119
}
87120

88-
if (file.path && !file.content) {
89-
file.dir = true
90-
return file
91-
}
121+
// add with object syntax { path : <string> , content: <Buffer|ReadableStream|PullStream|File }
122+
if (kindOf(file) === 'object') {
123+
// treat as an empty directory when path is a string and content undefined
124+
if (file.path && kindOf(file.path) === 'string' && !file.content) {
125+
file.dir = true
126+
return file
127+
}
92128

93-
if (file.content || file.dir) {
94-
return file
129+
// just return when directory
130+
if (file.dir) {
131+
return file
132+
}
133+
134+
if (file.content) {
135+
return {
136+
path: file.path || '',
137+
symlink: false,
138+
dir: false,
139+
content: contentToStream(file.content)
140+
}
141+
}
95142
}
96143

97144
return {
98145
path: '',
99146
symlink: false,
100147
dir: false,
101-
content: file
148+
content: contentToStream(file)
102149
}
103150
})
104151
}

0 commit comments

Comments
 (0)