mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-21 16:52:03 -07:00
feat(GET /ipfs/download/:cid): Download CID as readable stream
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"password": "yKm2qMy2LkRNFJsCiD2B",
|
"password": "BJdugaoiq10ZJnA7Uskd",
|
||||||
"email": "system@system.com",
|
"email": "system@system.com",
|
||||||
"id": "67bb5a07da0eb4a2e7fc7bde",
|
"id": "67c114fd4935ba05e48f6bb2",
|
||||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3YmI1YTA3ZGEwZWI0YTJlN2ZjN2JkZSIsImlhdCI6MTc0MDMzMTUyN30.o-MDjJpoq2MNElzWQsrWjy-kL_wji2hxPrXLPomrw7I"
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3YzExNGZkNDkzNWJhMDVlNDhmNmJiMiIsImlhdCI6MTc0MDcwNzA2OX0.R5nU8J5yhGorp7I5kc8_dRESbo4hwCIrTtqJiVYVhLA"
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,7 @@ class IpfsRESTControllerLib {
|
|||||||
this.getFileInfo = this.getFileInfo.bind(this)
|
this.getFileInfo = this.getFileInfo.bind(this)
|
||||||
this.getPins = this.getPins.bind(this)
|
this.getPins = this.getPins.bind(this)
|
||||||
this.cid2json = this.cid2json.bind(this)
|
this.cid2json = this.cid2json.bind(this)
|
||||||
|
this.downloadFile = this.downloadFile.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,12 +166,34 @@ class IpfsRESTControllerLib {
|
|||||||
)
|
)
|
||||||
ctx.body = readStream
|
ctx.body = readStream
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
|
||||||
console.log('Error in ipfs/controller.js/viewFile(): ', err)
|
console.log('Error in ipfs/controller.js/viewFile(): ', err)
|
||||||
this.handleError(ctx, err)
|
this.handleError(ctx, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /ipfs/download/:cid Download a file via its IPFS CID
|
||||||
|
* @apiPermission public
|
||||||
|
* @apiName GetCidDownload
|
||||||
|
* @apiGroup REST IPFS
|
||||||
|
* @apiDescription Download a file via its IPFS CID. Returns a readable stream.
|
||||||
|
*
|
||||||
|
* @apiExample Example usage:
|
||||||
|
* curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs/download/bafkreieaqtdhfywyddomswogynzymukosqqgqo7lkt5lch2zwfnc55m6om
|
||||||
|
*/
|
||||||
|
async downloadFile (ctx) {
|
||||||
|
try {
|
||||||
|
const { cid } = ctx.params
|
||||||
|
|
||||||
|
const { readStream } = await this.useCases.ipfs.downloadCid({ cid })
|
||||||
|
|
||||||
|
ctx.body = readStream
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error in ipfs/controller.js/downloadFile(): ', err)
|
||||||
|
this.handleError(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} /ipfs/service Get the IPFS ID for the File Pin service
|
* @api {get} /ipfs/service Get the IPFS ID for the File Pin service
|
||||||
* @apiPermission public
|
* @apiPermission public
|
||||||
@@ -192,7 +215,7 @@ class IpfsRESTControllerLib {
|
|||||||
selectedIpfsFileProvider
|
selectedIpfsFileProvider
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
// wlogger.error('Error in ipfs/controller.js/getService(): ', err)
|
||||||
console.log('Error in ipfs/controller.js/getService(): ', err)
|
console.log('Error in ipfs/controller.js/getService(): ', err)
|
||||||
this.handleError(ctx, err)
|
this.handleError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -220,7 +243,6 @@ class IpfsRESTControllerLib {
|
|||||||
|
|
||||||
ctx.body = metadata
|
ctx.body = metadata
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
|
||||||
console.log('Error in ipfs/controller.js/getFileInfo(): ', err)
|
console.log('Error in ipfs/controller.js/getFileInfo(): ', err)
|
||||||
this.handleError(ctx, err)
|
this.handleError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -248,7 +270,6 @@ class IpfsRESTControllerLib {
|
|||||||
|
|
||||||
ctx.body = pinData
|
ctx.body = pinData
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
|
||||||
console.log('Error in ipfs/controller.js/getPins(): ', err)
|
console.log('Error in ipfs/controller.js/getPins(): ', err)
|
||||||
this.handleError(ctx, err)
|
this.handleError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -274,7 +295,6 @@ class IpfsRESTControllerLib {
|
|||||||
|
|
||||||
ctx.body = json
|
ctx.body = json
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
|
||||||
console.log('Error in ipfs/controller.js/cid2json(): ', err.message)
|
console.log('Error in ipfs/controller.js/cid2json(): ', err.message)
|
||||||
this.handleError(ctx, err)
|
this.handleError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class IpfsRouter {
|
|||||||
this.router.post('/connect', this.ipfsRESTController.connect)
|
this.router.post('/connect', this.ipfsRESTController.connect)
|
||||||
this.router.get('/node', this.ipfsRESTController.getThisNode)
|
this.router.get('/node', this.ipfsRESTController.getThisNode)
|
||||||
this.router.get('/view/:cid', this.ipfsRESTController.viewFile)
|
this.router.get('/view/:cid', this.ipfsRESTController.viewFile)
|
||||||
|
this.router.get('/download/:cid', this.ipfsRESTController.downloadFile)
|
||||||
this.router.get('/service', this.ipfsRESTController.getService)
|
this.router.get('/service', this.ipfsRESTController.getService)
|
||||||
this.router.get('/file-info/:cid', this.ipfsRESTController.getFileInfo)
|
this.router.get('/file-info/:cid', this.ipfsRESTController.getFileInfo)
|
||||||
this.router.get('/pins', this.ipfsRESTController.getPins)
|
this.router.get('/pins', this.ipfsRESTController.getPins)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
// import { exporter } from 'ipfs-unixfs-exporter'
|
// import { exporter } from 'ipfs-unixfs-exporter'
|
||||||
import { Duplex } from 'stream'
|
import { Duplex } from 'stream'
|
||||||
import PSFFPP from 'psffpp'
|
import PSFFPP from 'psffpp'
|
||||||
|
import { CID } from 'multiformats'
|
||||||
|
|
||||||
// Local libraries
|
// Local libraries
|
||||||
// import PinEntity from '../entities/pin.js'
|
// import PinEntity from '../entities/pin.js'
|
||||||
@@ -28,11 +29,13 @@ class IpfsUseCases {
|
|||||||
|
|
||||||
// Encapsulate dependencies
|
// Encapsulate dependencies
|
||||||
this.psffpp = null // placeholder
|
this.psffpp = null // placeholder
|
||||||
|
this.CID = CID
|
||||||
|
|
||||||
// Bind 'this' object to all class subfunctions.
|
// Bind 'this' object to all class subfunctions.
|
||||||
this.downloadCid = this.downloadCid.bind(this)
|
this.downloadCid = this.downloadCid.bind(this)
|
||||||
// this.downloadCid2 = this.downloadCid2.bind(this)
|
// this.downloadCid2 = this.downloadCid2.bind(this)
|
||||||
this.getWritePrice = this.getWritePrice.bind(this)
|
this.getWritePrice = this.getWritePrice.bind(this)
|
||||||
|
this.getCidMetadata = this.getCidMetadata.bind(this)
|
||||||
this.cid2json = this.cid2json.bind(this)
|
this.cid2json = this.cid2json.bind(this)
|
||||||
|
|
||||||
// State
|
// State
|
||||||
@@ -43,7 +46,7 @@ class IpfsUseCases {
|
|||||||
// Returns a readable stream.
|
// Returns a readable stream.
|
||||||
async downloadCid (inObj = {}) {
|
async downloadCid (inObj = {}) {
|
||||||
try {
|
try {
|
||||||
const { cid } = inObj
|
let { cid } = inObj
|
||||||
|
|
||||||
if (!cid) throw new Error('CID is undefined')
|
if (!cid) throw new Error('CID is undefined')
|
||||||
|
|
||||||
@@ -64,6 +67,47 @@ class IpfsUseCases {
|
|||||||
|
|
||||||
const helia = this.adapters.ipfs.ipfs
|
const helia = this.adapters.ipfs.ipfs
|
||||||
|
|
||||||
|
const cidClass = this.CID.parse(cid)
|
||||||
|
|
||||||
|
// Download the file from other nodes.
|
||||||
|
await helia.blockstore.get(cidClass)
|
||||||
|
|
||||||
|
// list cid content, to dermine if this is a file-based or directory-based CID.
|
||||||
|
const contentArray = []
|
||||||
|
for await (const file of helia.fs.ls(cid)) {
|
||||||
|
contentArray.push(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle directory-based CIDs. This is used by Token Tiger for mutable and immutable token data.
|
||||||
|
/**
|
||||||
|
* If the cid is a directory
|
||||||
|
* The next block of code sends a html page with a list of links with the file names into the directory.
|
||||||
|
* Skipping this code delivers directly the first file detected in the directory.
|
||||||
|
*/
|
||||||
|
const isDir = contentArray[0].path.match('/') // TODO : looking for a better way to detect if is a directory
|
||||||
|
// 'listDir' is a flag to ignore this code on /download endpoint.
|
||||||
|
if (isDir) {
|
||||||
|
console.log('This CID is a directory. contentArray[0]: ', contentArray[0])
|
||||||
|
cid = `${cid}/${contentArray[0].name}`
|
||||||
|
|
||||||
|
// throw new Error('CID is a directory. Not supported yet.')
|
||||||
|
// const stream = new Stream.Readable({ read () { } })
|
||||||
|
// for (let i = 0; i < contentArray.length; i++) {
|
||||||
|
// const cont = contentArray[i]
|
||||||
|
// // List all paths excluding root path.
|
||||||
|
// if (cont.path !== cid) {
|
||||||
|
// // Add links to the gateway with the format cid/:filename
|
||||||
|
// stream.push(`<a href='${this.config.domainName}/ipfs/view/${cid}/${cont.name}' >/${cont.name} ( CID: ${cont.cid} )</a><hr />`)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// stream.push(null)
|
||||||
|
// // return fileName as html because the controller the library <mime.lookup> sends it as html
|
||||||
|
// return { readStream: stream }
|
||||||
|
} else {
|
||||||
|
console.log('This CID is not a directory.')
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the file to a Buffer.
|
// Convert the file to a Buffer.
|
||||||
const fileChunks = []
|
const fileChunks = []
|
||||||
for await (const chunk of helia.fs.cat(cid)) {
|
for await (const chunk of helia.fs.cat(cid)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user