1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-08-06 19:44:01 +08:00
Files
kubo/core/commands/get.go
Henry f640ba0089 core: add context.Context param to core.Resolve()
commands/object: remove objectData() and objectLinks() helpers
resolver: added context parameters
sharness: $HASH carried the \r from the http protocol with
sharness: write curl output to individual files
http gw: break PUT handler until PR#1191
2015-05-08 03:14:32 +02:00

178 lines
4.6 KiB
Go

package commands
import (
"compress/gzip"
"errors"
"fmt"
"io"
"os"
gopath "path"
"strings"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
cmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
path "github.com/ipfs/go-ipfs/path"
tar "github.com/ipfs/go-ipfs/thirdparty/tar"
utar "github.com/ipfs/go-ipfs/unixfs/tar"
)
var ErrInvalidCompressionLevel = errors.New("Compression level must be between 1 and 9")
var GetCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Download IPFS objects",
ShortDescription: `
Retrieves the object named by <ipfs-or-ipns-path> and stores the data to disk.
By default, the output will be stored at ./<ipfs-path>, but an alternate path
can be specified with '--output=<path>' or '-o=<path>'.
To output a TAR archive instead of unpacked files, use '--archive' or '-a'.
To compress the output with GZIP compression, use '--compress' or '-C'. You
may also specify the level of compression by specifying '-l=<1-9>'.
`,
},
Arguments: []cmds.Argument{
cmds.StringArg("ipfs-path", true, false, "The path to the IPFS object(s) to be outputted").EnableStdin(),
},
Options: []cmds.Option{
cmds.StringOption("output", "o", "The path where output should be stored"),
cmds.BoolOption("archive", "a", "Output a TAR archive"),
cmds.BoolOption("compress", "C", "Compress the output with GZIP compression"),
cmds.IntOption("compression-level", "l", "The level of compression (1-9)"),
},
PreRun: func(req cmds.Request) error {
_, err := getCompressOptions(req)
return err
},
Run: func(req cmds.Request, res cmds.Response) {
cmplvl, err := getCompressOptions(req)
if err != nil {
res.SetError(err, cmds.ErrClient)
return
}
node, err := req.Context().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
reader, err := get(req.Context().Context, node, req.Arguments()[0], cmplvl)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
res.SetOutput(reader)
},
PostRun: func(req cmds.Request, res cmds.Response) {
if res.Output() == nil {
return
}
outReader := res.Output().(io.Reader)
res.SetOutput(nil)
outPath, _, _ := req.Option("output").String()
if len(outPath) == 0 {
_, outPath = gopath.Split(req.Arguments()[0])
outPath = gopath.Clean(outPath)
}
cmplvl, err := getCompressOptions(req)
if err != nil {
res.SetError(err, cmds.ErrClient)
return
}
if archive, _, _ := req.Option("archive").Bool(); archive {
if !strings.HasSuffix(outPath, ".tar") {
outPath += ".tar"
}
if cmplvl != gzip.NoCompression {
outPath += ".gz"
}
fmt.Printf("Saving archive to %s\n", outPath)
file, err := os.Create(outPath)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
defer file.Close()
bar := pb.New(0).SetUnits(pb.U_BYTES)
bar.Output = os.Stderr
pbReader := bar.NewProxyReader(outReader)
bar.Start()
defer bar.Finish()
_, err = io.Copy(file, pbReader)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
return
}
fmt.Printf("Saving file(s) to %s\n", outPath)
// TODO: get total length of files
bar := pb.New(0).SetUnits(pb.U_BYTES)
bar.Output = os.Stderr
// wrap the reader with the progress bar proxy reader
// if the output is compressed, also wrap it in a gzip.Reader
var reader io.Reader
if cmplvl != gzip.NoCompression {
gzipReader, err := gzip.NewReader(outReader)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
defer gzipReader.Close()
reader = bar.NewProxyReader(gzipReader)
} else {
reader = bar.NewProxyReader(outReader)
}
bar.Start()
defer bar.Finish()
extractor := &tar.Extractor{outPath}
err = extractor.Extract(reader)
if err != nil {
res.SetError(err, cmds.ErrNormal)
}
},
}
func getCompressOptions(req cmds.Request) (int, error) {
cmprs, _, _ := req.Option("compress").Bool()
cmplvl, cmplvlFound, _ := req.Option("compression-level").Int()
switch {
case !cmprs:
return gzip.NoCompression, nil
case cmprs && !cmplvlFound:
return gzip.DefaultCompression, nil
case cmprs && cmplvlFound && (cmplvl < 1 || cmplvl > 9):
return gzip.NoCompression, ErrInvalidCompressionLevel
}
return gzip.NoCompression, nil
}
func get(ctx context.Context, node *core.IpfsNode, p string, compression int) (io.Reader, error) {
pathToResolve := path.Path(p)
dagnode, err := core.Resolve(ctx, node, pathToResolve)
if err != nil {
return nil, err
}
return utar.NewReader(pathToResolve, node.DAG, dagnode, compression)
}