mirror of
https://github.com/ipfs/kubo.git
synced 2025-08-06 19:44:01 +08:00
119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
|
|
core "github.com/ipfs/go-ipfs/core"
|
|
coreunix "github.com/ipfs/go-ipfs/core/coreunix"
|
|
|
|
cmds "gx/ipfs/QmP9vZfc5WSjfGTXmwX2EcicMFzmZ6fXn7HTdKYat6ccmH/go-ipfs-cmds"
|
|
"gx/ipfs/QmQp2a2Hhb7F6eK2A5hN8f9aJy4mtkEikL9Zj4cgB7d1dD/go-ipfs-cmdkit"
|
|
)
|
|
|
|
const progressBarMinSize = 1024 * 1024 * 8 // show progress bar for outputs > 8MiB
|
|
|
|
var CatCmd = &cmds.Command{
|
|
Helptext: cmdkit.HelpText{
|
|
Tagline: "Show IPFS object data.",
|
|
ShortDescription: "Displays the data contained by an IPFS or IPNS object(s) at the given path.",
|
|
},
|
|
|
|
Arguments: []cmdkit.Argument{
|
|
cmdkit.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted.").EnableStdin(),
|
|
},
|
|
Run: func(req cmds.Request, res cmds.ResponseEmitter) {
|
|
node, err := req.InvocContext().GetNode()
|
|
if err != nil {
|
|
res.SetError(err, cmdkit.ErrNormal)
|
|
return
|
|
}
|
|
|
|
if !node.OnlineMode() {
|
|
if err := node.SetupOfflineRouting(); err != nil {
|
|
res.SetError(err, cmdkit.ErrNormal)
|
|
return
|
|
}
|
|
}
|
|
|
|
readers, length, err := cat(req.Context(), node, req.Arguments())
|
|
if err != nil {
|
|
res.SetError(err, cmdkit.ErrNormal)
|
|
return
|
|
}
|
|
|
|
/*
|
|
if err := corerepo.ConditionalGC(req.Context(), node, length); err != nil {
|
|
re.SetError(err, cmdkit.ErrNormal)
|
|
return
|
|
}
|
|
*/
|
|
|
|
res.SetLength(length)
|
|
reader := io.MultiReader(readers...)
|
|
|
|
// Since the reader returns the error that a block is missing, and that error is
|
|
// returned from io.Copy inside Emit, we need to take Emit errors and send
|
|
// them to the client. Usually we don't do that because it means the connection
|
|
// is broken or we supplied an illegal argument etc.
|
|
err = res.Emit(reader)
|
|
if err != nil {
|
|
res.SetError(err, cmdkit.ErrNormal)
|
|
}
|
|
},
|
|
PostRun: map[cmds.EncodingType]func(cmds.Request, cmds.ResponseEmitter) cmds.ResponseEmitter{
|
|
cmds.CLI: func(req cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
|
|
reNext, res := cmds.NewChanResponsePair(req)
|
|
|
|
go func() {
|
|
if res.Length() > 0 && res.Length() < progressBarMinSize {
|
|
if err := cmds.Copy(re, res); err != nil {
|
|
re.SetError(err, cmdkit.ErrNormal)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Copy closes by itself, so we must not do this before
|
|
defer re.Close()
|
|
for {
|
|
v, err := res.Next()
|
|
if !cmds.HandleError(err, res, re) {
|
|
break
|
|
}
|
|
|
|
switch val := v.(type) {
|
|
case io.Reader:
|
|
bar, reader := progressBarForReader(os.Stderr, val, int64(res.Length()))
|
|
bar.Start()
|
|
|
|
err = re.Emit(reader)
|
|
if err != nil {
|
|
log.Error(err)
|
|
}
|
|
default:
|
|
log.Warningf("cat postrun: received unexpected type %T", val)
|
|
}
|
|
}
|
|
}()
|
|
|
|
return reNext
|
|
},
|
|
},
|
|
}
|
|
|
|
func cat(ctx context.Context, node *core.IpfsNode, paths []string) ([]io.Reader, uint64, error) {
|
|
readers := make([]io.Reader, 0, len(paths))
|
|
length := uint64(0)
|
|
for _, fpath := range paths {
|
|
read, err := coreunix.Cat(ctx, node, fpath)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
readers = append(readers, read)
|
|
length += uint64(read.Size())
|
|
}
|
|
return readers, length, nil
|
|
}
|