diff --git a/bzz/api.go b/bzz/api.go index d0d8965d6e..019104776f 100644 --- a/bzz/api.go +++ b/bzz/api.go @@ -1,6 +1,7 @@ package bzz import ( + "bufio" "bytes" "fmt" "io" @@ -179,8 +180,62 @@ func (self *Api) Modify(rootHash, path, contentHash, contentType string) (newRoo // Download replicates the manifest path structure on the local filesystem // under localpath -func (self *Api) Download(bzzpath, localpath string) (string, error) { - return "", nil +func (self *Api) Download(bzzpath, localpath string) (err error) { + lpath, err := filepath.Abs(filepath.Clean(localpath)) + if err != nil { + return + } + err = os.MkdirAll(lpath, os.ModePerm) + if err != nil { + return + } + + parts := slashes.Split(bzzpath, 3) + if len(parts) < 2 { + return fmt.Errorf("Invalid bzz path") + } + hostPort := parts[1] + var path string + if len(parts) > 2 { + path = regularSlashes(parts[2]) + "/" + } + dpaLogger.Debugf("Swarm: host: '%s', path '%s' requested.", hostPort, path) + + //resolving host and port + var key Key + key, err = self.Resolve(hostPort) + if err != nil { + err = errResolve(err) + dpaLogger.Debugf("Swarm: error : %v", err) + return + } + + trie, err := loadManifest(self.dpa, key) + if err != nil { + dpaLogger.Debugf("Swarm: loadManifestTrie error: %v", err) + return + } + + prevPath := lpath + trie.listWithPrefix(path, func(entry *manifestTrieEntry, suffix string) { // TODO: paralellize + key := common.Hex2Bytes(entry.Hash) + reader := self.dpa.Retrieve(key) + path := lpath + "/" + suffix + dir := filepath.Dir(path) + if dir != prevPath { + os.MkdirAll(dir, os.ModePerm) // TODO: handle errors + prevPath = dir + } + f, _ := os.Create(path) // TODO: handle errors, ??path separators + writer := bufio.NewWriter(f) + //io.Copy(writer, reader) // TODO: handle errors + io.CopyN(writer, reader, reader.Size()) // TODO: handle errors + + writer.Flush() + f.Close() + }) + + return } const maxParallelFiles = 5 diff --git a/bzz/js_api.go b/bzz/js_api.go index 4bea187b06..a405d34153 100644 --- a/bzz/js_api.go +++ b/bzz/js_api.go @@ -159,7 +159,7 @@ func (self *JSApi) download(call otto.FunctionCall) otto.Value { } var err error - var bzzpath, localpath, res string + var bzzpath, localpath string bzzpath, err = call.Argument(0).ToString() if err != nil { fmt.Println(err) @@ -171,14 +171,13 @@ func (self *JSApi) download(call otto.FunctionCall) otto.Value { return otto.UndefinedValue() } - res, err = self.api.Download(bzzpath, localpath) + err = self.api.Download(bzzpath, localpath) if err != nil { fmt.Println(err) return otto.UndefinedValue() } - v, _ := call.Otto.ToValue(res) - return v + return otto.UndefinedValue() } func (self *JSApi) upload(call otto.FunctionCall) otto.Value { diff --git a/bzz/manifest.go b/bzz/manifest.go index 579ced1f23..02c333b207 100644 --- a/bzz/manifest.go +++ b/bzz/manifest.go @@ -209,6 +209,42 @@ func (self *manifestTrie) loadSubTrie(entry *manifestTrieEntry) (err error) { return } +func (self *manifestTrie) listWithPrefixInt(prefix, rp string, cb func(entry *manifestTrieEntry, suffix string)) { + plen := len(prefix) + var start, stop int + if plen == 0 { + start = 0 + stop = 256 + } else { + start = int(prefix[0]) + stop = start + } + + for i := start; i <= stop; i++ { + entry := self.entries[i] + if entry != nil { + epl := len(entry.Path) + if entry.ContentType == manifestType { + l := plen + if epl < l { + l = epl + } + if (prefix[:l] == entry.Path[:l]) && (self.loadSubTrie(entry) == nil) { // TODO: handle errors + entry.subtrie.listWithPrefixInt(prefix[l:], rp+entry.Path[l:], cb) + } + } else { + if (epl >= plen) && (prefix == entry.Path[:plen]) { + cb(entry, rp+entry.Path[plen:]) + } + } + } + } +} + +func (self *manifestTrie) listWithPrefix(prefix string, cb func(entry *manifestTrieEntry, suffix string)) { + self.listWithPrefixInt(prefix, "", cb) +} + func (self *manifestTrie) findPrefixOf(path string) (entry *manifestTrieEntry, pos int) { dpaLogger.Debugf("findPrefixOf(%s)", path)