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

Commit

Permalink
feat: add support for File DOM API to files-regular (#986)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hugomrdias authored and Alan Shaw committed May 16, 2019
1 parent 8a4062a commit 7b49f7e
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .aegir.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const createServer = require('ipfsd-ctl').createServer
const server = createServer()

module.exports = {
bundlesize: { maxSize: '231kB' },
bundlesize: { maxSize: '232kB' },
webpack: {
resolve: {
mainFields: ['browser', 'main']
Expand Down
11 changes: 0 additions & 11 deletions examples/upload-file-via-browser/.eslintrc

This file was deleted.

1 change: 1 addition & 0 deletions examples/upload-file-via-browser/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
npm-debug.log
.DS_Store
dist
yarn.lock
26 changes: 19 additions & 7 deletions examples/upload-file-via-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "Upload file to IPFS via browser using js-ipfs-http-client with Webpack",
"scripts": {
"start": "node server.js"
"start": "webpack-dev-server"
},
"author": "Harlan T Wood <code@harlantwood.net>",
"contributors": [
Expand All @@ -12,12 +12,24 @@
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.4.3",
"ipfs-http-client": "../../",
"pull-file-reader": "~1.0.2",
"react": "~16.8.6",
"react-dom": "~16.8.6",
"react-hot-loader": "~4.8.4",
"webpack": "~4.31.0",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"eslint": "^5.16.0",
"eslint-plugin-react": "^7.11.1",
"react": "~16.6.3",
"react-dom": "~16.6.3",
"webpack": "~4.30.0",
"webpack-dev-server": "~3.3.1"
},
"eslintConfig" : {
"extends": "standard",
"rules": {
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2
},
"plugins": [
"react"
]
}
}
13 changes: 0 additions & 13 deletions examples/upload-file-via-browser/server.js

This file was deleted.

27 changes: 11 additions & 16 deletions examples/upload-file-via-browser/src/App.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
'use strict'
const React = require('react')
const ipfsClient = require('ipfs-http-client')

// create a stream from a file, which enables uploads of big files without allocating memory twice
const fileReaderPullStream = require('pull-file-reader')
const ipfsClient = require('../../../src')

class App extends React.Component {
constructor () {
super()
this.state = {
added_file_hash: null
}
this.ipfs = ipfsClient('localhost', '5001')
this.ipfs = ipfsClient('localhost', '58041')

// bind methods
this.captureFile = this.captureFile.bind(this)
Expand All @@ -22,38 +19,36 @@ class App extends React.Component {
captureFile (event) {
event.stopPropagation()
event.preventDefault()
const file = event.target.files[0]
if (document.getElementById('keepFilename').checked) {
this.saveToIpfsWithFilename(file)
this.saveToIpfsWithFilename(event.target.files)
} else {
this.saveToIpfs(file)
this.saveToIpfs(event.target.files)
}
}

// Example #1
// Add file to IPFS and return a CID
saveToIpfs (file) {
saveToIpfs (files) {
let ipfsId
const fileStream = fileReaderPullStream(file)
this.ipfs.add(fileStream, { progress: (prog) => console.log(`received: ${prog}`) })
this.ipfs.add([...files], { progress: (prog) => console.log(`received: ${prog}`) })
.then((response) => {
console.log(response)
ipfsId = response[0].hash
console.log(ipfsId)
this.setState({added_file_hash: ipfsId})
this.setState({ added_file_hash: ipfsId })
}).catch((err) => {
console.error(err)
})
}

// Example #2
// Add file to IPFS and wrap it in a directory to keep the original filename
saveToIpfsWithFilename (file) {
saveToIpfsWithFilename (files) {
const file = [...files][0]
let ipfsId
const fileStream = fileReaderPullStream(file)
const fileDetails = {
path: file.name,
content: fileStream
content: file
}
const options = {
wrapWithDirectory: true,
Expand All @@ -65,7 +60,7 @@ class App extends React.Component {
// CID of wrapping directory is returned last
ipfsId = response[response.length - 1].hash
console.log(ipfsId)
this.setState({added_file_hash: ipfsId})
this.setState({ added_file_hash: ipfsId })
}).catch((err) => {
console.error(err)
})
Expand Down
9 changes: 1 addition & 8 deletions examples/upload-file-via-browser/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
'use strict'

const path = require('path')
const webpack = require('webpack')

module.exports = {
mode: 'development',
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
Expand All @@ -28,8 +22,7 @@ module.exports = {
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: ['react-hot-loader/babel']
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
]
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@
"is-stream": "^2.0.0",
"iso-stream-http": "~0.1.2",
"iso-url": "~0.4.6",
"ipfs-utils": "~0.0.3",
"just-kebab-case": "^1.1.0",
"just-map-keys": "^1.1.0",
"kind-of": "^6.0.2",
"lru-cache": "^5.1.1",
"multiaddr": "^6.0.6",
"multibase": "~0.6.0",
Expand Down Expand Up @@ -83,9 +85,8 @@
"chai": "^4.2.0",
"cross-env": "^5.2.0",
"dirty-chai": "^2.0.1",
"eslint-plugin-react": "^7.11.1",
"go-ipfs-dep": "0.4.19",
"interface-ipfs-core": "~0.101.1",
"interface-ipfs-core": "~0.102.0",
"ipfsd-ctl": "~0.42.0",
"nock": "^10.0.2",
"stream-equal": "^1.1.1"
Expand Down
26 changes: 6 additions & 20 deletions src/files-regular/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
const promisify = require('promisify-es6')
const ConcatStream = require('concat-stream')
const once = require('once')
const isStream = require('is-stream')
const isSource = require('is-pull-stream').isSource
const { isSource } = require('is-pull-stream')
const FileResultStreamConverter = require('../utils/file-result-stream-converter')
const SendFilesStream = require('../utils/send-files-stream')
const validateAddInput = require('ipfs-utils/src/files/add-input-validation')

module.exports = (send) => {
const createAddStream = SendFilesStream(send, 'add')
Expand All @@ -16,31 +16,17 @@ module.exports = (send) => {
_callback = options
options = null
}

const callback = once(_callback)

if (!options) {
options = {}
}
options.converter = FileResultStreamConverter

// Buffer, pull stream or Node.js stream
const isBufferOrStream = obj => Buffer.isBuffer(obj) || isStream.readable(obj) || isSource(obj)
// An object like { content?, path? }, where content isBufferOrStream and path isString
const isContentObject = obj => {
if (typeof obj !== 'object') return false
// path is optional if content is present
if (obj.content) return isBufferOrStream(obj.content)
// path must be a non-empty string if no content
return Boolean(obj.path) && typeof obj.path === 'string'
}
// An input atom: a buffer, stream or content object
const isInput = obj => isBufferOrStream(obj) || isContentObject(obj)
// All is ok if data isInput or data is an array of isInput
const ok = isInput(_files) || (Array.isArray(_files) && _files.every(isInput))

if (!ok) {
return callback(new Error('invalid input: expected buffer, readable stream, pull stream, object or array of objects'))
try {
validateAddInput(_files)
} catch (err) {
return callback(err)
}

const files = [].concat(_files)
Expand Down
7 changes: 0 additions & 7 deletions src/utils/multipart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

const Transform = require('readable-stream').Transform
const isNode = require('detect-node')
const isSource = require('is-pull-stream').isSource
const toStream = require('pull-to-stream')

const PADDING = '--'
const NEW_LINE = '\r\n'
Expand Down Expand Up @@ -77,12 +75,7 @@ class Multipart extends Transform {
return callback() // early
}

if (isSource(content)) {
content = toStream.readable(content)
}

// From now on we assume content is a stream

content.once('error', this.emit.bind(this, 'error'))

content.once('end', () => {
Expand Down
61 changes: 54 additions & 7 deletions src/utils/prepare-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

const isNode = require('detect-node')
const flatmap = require('flatmap')
const { Readable } = require('readable-stream')
const kindOf = require('kind-of')
const { isSource } = require('is-pull-stream')
const isStream = require('is-stream')
const pullToStream = require('pull-to-stream')
const { supportsFileReader } = require('ipfs-utils/src/supports')
const streamFromFileReader = require('ipfs-utils/src/streams/stream-from-filereader')

function loadPaths (opts, file) {
const path = require('path')
Expand Down Expand Up @@ -73,10 +80,36 @@ function loadPaths (opts, file) {
}
}

function contentToStream (content) {
if (supportsFileReader && kindOf(content) === 'file') {
return streamFromFileReader(content)
}

if (kindOf(content) === 'buffer') {
return new Readable({
read () {
this.push(content)
this.push(null)
}
})
}

if (isSource(content)) {
return pullToStream.readable(content)
}

if (isStream.readable(content)) {
return content
}

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`)
}

function prepareFile (file, opts) {
let files = [].concat(file)

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

if (file.path && !file.content) {
file.dir = true
return file
}
// add with object syntax { path : <string> , content: <Buffer|ReadableStream|PullStream|File }
if (kindOf(file) === 'object') {
// treat as an empty directory when path is a string and content undefined
if (file.path && kindOf(file.path) === 'string' && !file.content) {
file.dir = true
return file
}

if (file.content || file.dir) {
return file
// just return when directory
if (file.dir) {
return file
}

if (file.content) {
return {
path: file.path || '',
symlink: false,
dir: false,
content: contentToStream(file.content)
}
}
}

return {
path: '',
symlink: false,
dir: false,
content: file
content: contentToStream(file)
}
})
}
Expand Down

0 comments on commit 7b49f7e

Please sign in to comment.