github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/filesystem.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:43</date> 10 //</624450111585390592> 11 12 13 package api 14 15 import ( 16 "bufio" 17 "context" 18 "fmt" 19 "io" 20 "os" 21 "path" 22 "path/filepath" 23 "sync" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/swarm/log" 27 "github.com/ethereum/go-ethereum/swarm/storage" 28 ) 29 30 const maxParallelFiles = 5 31 32 type FileSystem struct { 33 api *API 34 } 35 36 func NewFileSystem(api *API) *FileSystem { 37 return &FileSystem{api} 38 } 39 40 //upload将本地目录复制为清单文件并将其上载 41 //使用文件存储存储 42 //此函数等待存储块。 43 //TODO:本地路径应指向清单 44 // 45 //已弃用:请改用HTTP API 46 func (fs *FileSystem) Upload(lpath, index string, toEncrypt bool) (string, error) { 47 var list []*manifestTrieEntry 48 localpath, err := filepath.Abs(filepath.Clean(lpath)) 49 if err != nil { 50 return "", err 51 } 52 53 f, err := os.Open(localpath) 54 if err != nil { 55 return "", err 56 } 57 stat, err := f.Stat() 58 if err != nil { 59 return "", err 60 } 61 62 var start int 63 if stat.IsDir() { 64 start = len(localpath) 65 log.Debug(fmt.Sprintf("uploading '%s'", localpath)) 66 err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { 67 if (err == nil) && !info.IsDir() { 68 if len(path) <= start { 69 return fmt.Errorf("Path is too short") 70 } 71 if path[:start] != localpath { 72 return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) 73 } 74 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(path)}, nil) 75 list = append(list, entry) 76 } 77 return err 78 }) 79 if err != nil { 80 return "", err 81 } 82 } else { 83 dir := filepath.Dir(localpath) 84 start = len(dir) 85 if len(localpath) <= start { 86 return "", fmt.Errorf("Path is too short") 87 } 88 if localpath[:start] != dir { 89 return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) 90 } 91 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(localpath)}, nil) 92 list = append(list, entry) 93 } 94 95 errors := make([]error, len(list)) 96 sem := make(chan bool, maxParallelFiles) 97 defer close(sem) 98 99 for i, entry := range list { 100 sem <- true 101 go func(i int, entry *manifestTrieEntry) { 102 defer func() { <-sem }() 103 104 f, err := os.Open(entry.Path) 105 if err != nil { 106 errors[i] = err 107 return 108 } 109 defer f.Close() 110 111 stat, err := f.Stat() 112 if err != nil { 113 errors[i] = err 114 return 115 } 116 117 var hash storage.Address 118 var wait func(context.Context) error 119 ctx := context.TODO() 120 hash, wait, err = fs.api.fileStore.Store(ctx, f, stat.Size(), toEncrypt) 121 if err != nil { 122 errors[i] = err 123 return 124 } 125 if hash != nil { 126 list[i].Hash = hash.Hex() 127 } 128 if err := wait(ctx); err != nil { 129 errors[i] = err 130 return 131 } 132 133 list[i].ContentType, err = DetectContentType(f.Name(), f) 134 if err != nil { 135 errors[i] = err 136 return 137 } 138 139 }(i, entry) 140 } 141 for i := 0; i < cap(sem); i++ { 142 sem <- true 143 } 144 145 trie := &manifestTrie{ 146 fileStore: fs.api.fileStore, 147 } 148 quitC := make(chan bool) 149 for i, entry := range list { 150 if errors[i] != nil { 151 return "", errors[i] 152 } 153 entry.Path = RegularSlashes(entry.Path[start:]) 154 if entry.Path == index { 155 ientry := newManifestTrieEntry(&ManifestEntry{ 156 ContentType: entry.ContentType, 157 }, nil) 158 ientry.Hash = entry.Hash 159 trie.addEntry(ientry, quitC) 160 } 161 trie.addEntry(entry, quitC) 162 } 163 164 err2 := trie.recalcAndStore() 165 var hs string 166 if err2 == nil { 167 hs = trie.ref.Hex() 168 } 169 return hs, err2 170 } 171 172 //下载复制本地文件系统上的清单basepath结构 173 //在局部路径下 174 // 175 //已弃用:请改用HTTP API 176 func (fs *FileSystem) Download(bzzpath, localpath string) error { 177 lpath, err := filepath.Abs(filepath.Clean(localpath)) 178 if err != nil { 179 return err 180 } 181 err = os.MkdirAll(lpath, os.ModePerm) 182 if err != nil { 183 return err 184 } 185 186 //解析主机和端口 187 uri, err := Parse(path.Join("bzz:/", bzzpath)) 188 if err != nil { 189 return err 190 } 191 addr, err := fs.api.Resolve(context.TODO(), uri.Addr) 192 if err != nil { 193 return err 194 } 195 path := uri.Path 196 197 if len(path) > 0 { 198 path += "/" 199 } 200 201 quitC := make(chan bool) 202 trie, err := loadManifest(context.TODO(), fs.api.fileStore, addr, quitC, NOOPDecrypt) 203 if err != nil { 204 log.Warn(fmt.Sprintf("fs.Download: loadManifestTrie error: %v", err)) 205 return err 206 } 207 208 type downloadListEntry struct { 209 addr storage.Address 210 path string 211 } 212 213 var list []*downloadListEntry 214 var mde error 215 216 prevPath := lpath 217 err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { 218 log.Trace(fmt.Sprintf("fs.Download: %#v", entry)) 219 220 addr = common.Hex2Bytes(entry.Hash) 221 path := lpath + "/" + suffix 222 dir := filepath.Dir(path) 223 if dir != prevPath { 224 mde = os.MkdirAll(dir, os.ModePerm) 225 prevPath = dir 226 } 227 if (mde == nil) && (path != dir+"/") { 228 list = append(list, &downloadListEntry{addr: addr, path: path}) 229 } 230 }) 231 if err != nil { 232 return err 233 } 234 235 wg := sync.WaitGroup{} 236 errC := make(chan error) 237 done := make(chan bool, maxParallelFiles) 238 for i, entry := range list { 239 select { 240 case done <- true: 241 wg.Add(1) 242 case <-quitC: 243 return fmt.Errorf("aborted") 244 } 245 go func(i int, entry *downloadListEntry) { 246 defer wg.Done() 247 err := retrieveToFile(quitC, fs.api.fileStore, entry.addr, entry.path) 248 if err != nil { 249 select { 250 case errC <- err: 251 case <-quitC: 252 } 253 return 254 } 255 <-done 256 }(i, entry) 257 } 258 go func() { 259 wg.Wait() 260 close(errC) 261 }() 262 select { 263 case err = <-errC: 264 return err 265 case <-quitC: 266 return fmt.Errorf("aborted") 267 } 268 } 269 270 func retrieveToFile(quitC chan bool, fileStore *storage.FileStore, addr storage.Address, path string) error { 271 f, err := os.Create(path) //TODO:基本路径分隔符 272 if err != nil { 273 return err 274 } 275 reader, _ := fileStore.Retrieve(context.TODO(), addr) 276 writer := bufio.NewWriter(f) 277 size, err := reader.Size(context.TODO(), quitC) 278 if err != nil { 279 return err 280 } 281 if _, err = io.CopyN(writer, reader, size); err != nil { 282 return err 283 } 284 if err := writer.Flush(); err != nil { 285 return err 286 } 287 return f.Close() 288 } 289