github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/swarm/api/filesystem.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 // 10 // 11 // 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 25 package api 26 27 import ( 28 "bufio" 29 "context" 30 "fmt" 31 "io" 32 "net/http" 33 "os" 34 "path" 35 "path/filepath" 36 "sync" 37 38 "github.com/ethereum/go-ethereum/common" 39 "github.com/ethereum/go-ethereum/swarm/log" 40 "github.com/ethereum/go-ethereum/swarm/storage" 41 ) 42 43 const maxParallelFiles = 5 44 45 type FileSystem struct { 46 api *API 47 } 48 49 func NewFileSystem(api *API) *FileSystem { 50 return &FileSystem{api} 51 } 52 53 // 54 // 55 // 56 // 57 // 58 // 59 func (fs *FileSystem) Upload(lpath, index string, toEncrypt bool) (string, error) { 60 var list []*manifestTrieEntry 61 localpath, err := filepath.Abs(filepath.Clean(lpath)) 62 if err != nil { 63 return "", err 64 } 65 66 f, err := os.Open(localpath) 67 if err != nil { 68 return "", err 69 } 70 stat, err := f.Stat() 71 if err != nil { 72 return "", err 73 } 74 75 var start int 76 if stat.IsDir() { 77 start = len(localpath) 78 log.Debug(fmt.Sprintf("uploading '%s'", localpath)) 79 err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { 80 if (err == nil) && !info.IsDir() { 81 if len(path) <= start { 82 return fmt.Errorf("Path is too short") 83 } 84 if path[:start] != localpath { 85 return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) 86 } 87 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(path)}, nil) 88 list = append(list, entry) 89 } 90 return err 91 }) 92 if err != nil { 93 return "", err 94 } 95 } else { 96 dir := filepath.Dir(localpath) 97 start = len(dir) 98 if len(localpath) <= start { 99 return "", fmt.Errorf("Path is too short") 100 } 101 if localpath[:start] != dir { 102 return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) 103 } 104 entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(localpath)}, nil) 105 list = append(list, entry) 106 } 107 108 cnt := len(list) 109 errors := make([]error, cnt) 110 done := make(chan bool, maxParallelFiles) 111 dcnt := 0 112 awg := &sync.WaitGroup{} 113 114 for i, entry := range list { 115 if i >= dcnt+maxParallelFiles { 116 <-done 117 dcnt++ 118 } 119 awg.Add(1) 120 go func(i int, entry *manifestTrieEntry, done chan bool) { 121 f, err := os.Open(entry.Path) 122 if err == nil { 123 stat, _ := f.Stat() 124 var hash storage.Address 125 var wait func(context.Context) error 126 ctx := context.TODO() 127 hash, wait, err = fs.api.fileStore.Store(ctx, f, stat.Size(), toEncrypt) 128 if hash != nil { 129 list[i].Hash = hash.Hex() 130 } 131 err = wait(ctx) 132 awg.Done() 133 if err == nil { 134 first512 := make([]byte, 512) 135 fread, _ := f.ReadAt(first512, 0) 136 if fread > 0 { 137 mimeType := http.DetectContentType(first512[:fread]) 138 if filepath.Ext(entry.Path) == ".css" { 139 mimeType = "text/css" 140 } 141 list[i].ContentType = mimeType 142 } 143 } 144 f.Close() 145 } 146 errors[i] = err 147 done <- true 148 }(i, entry, done) 149 } 150 for dcnt < cnt { 151 <-done 152 dcnt++ 153 } 154 155 trie := &manifestTrie{ 156 fileStore: fs.api.fileStore, 157 } 158 quitC := make(chan bool) 159 for i, entry := range list { 160 if errors[i] != nil { 161 return "", errors[i] 162 } 163 entry.Path = RegularSlashes(entry.Path[start:]) 164 if entry.Path == index { 165 ientry := newManifestTrieEntry(&ManifestEntry{ 166 ContentType: entry.ContentType, 167 }, nil) 168 ientry.Hash = entry.Hash 169 trie.addEntry(ientry, quitC) 170 } 171 trie.addEntry(entry, quitC) 172 } 173 174 err2 := trie.recalcAndStore() 175 var hs string 176 if err2 == nil { 177 hs = trie.ref.Hex() 178 } 179 awg.Wait() 180 return hs, err2 181 } 182 183 // 184 // 185 // 186 // 187 func (fs *FileSystem) Download(bzzpath, localpath string) error { 188 lpath, err := filepath.Abs(filepath.Clean(localpath)) 189 if err != nil { 190 return err 191 } 192 err = os.MkdirAll(lpath, os.ModePerm) 193 if err != nil { 194 return err 195 } 196 197 // 198 uri, err := Parse(path.Join("bzz:/", bzzpath)) 199 if err != nil { 200 return err 201 } 202 addr, err := fs.api.Resolve(context.TODO(), uri.Addr) 203 if err != nil { 204 return err 205 } 206 path := uri.Path 207 208 if len(path) > 0 { 209 path += "/" 210 } 211 212 quitC := make(chan bool) 213 trie, err := loadManifest(context.TODO(), fs.api.fileStore, addr, quitC, NOOPDecrypt) 214 if err != nil { 215 log.Warn(fmt.Sprintf("fs.Download: loadManifestTrie error: %v", err)) 216 return err 217 } 218 219 type downloadListEntry struct { 220 addr storage.Address 221 path string 222 } 223 224 var list []*downloadListEntry 225 var mde error 226 227 prevPath := lpath 228 err = trie.listWithPrefix(path, quitC, func(entry *manifestTrieEntry, suffix string) { 229 log.Trace(fmt.Sprintf("fs.Download: %#v", entry)) 230 231 addr = common.Hex2Bytes(entry.Hash) 232 path := lpath + "/" + suffix 233 dir := filepath.Dir(path) 234 if dir != prevPath { 235 mde = os.MkdirAll(dir, os.ModePerm) 236 prevPath = dir 237 } 238 if (mde == nil) && (path != dir+"/") { 239 list = append(list, &downloadListEntry{addr: addr, path: path}) 240 } 241 }) 242 if err != nil { 243 return err 244 } 245 246 wg := sync.WaitGroup{} 247 errC := make(chan error) 248 done := make(chan bool, maxParallelFiles) 249 for i, entry := range list { 250 select { 251 case done <- true: 252 wg.Add(1) 253 case <-quitC: 254 return fmt.Errorf("aborted") 255 } 256 go func(i int, entry *downloadListEntry) { 257 defer wg.Done() 258 err := retrieveToFile(quitC, fs.api.fileStore, entry.addr, entry.path) 259 if err != nil { 260 select { 261 case errC <- err: 262 case <-quitC: 263 } 264 return 265 } 266 <-done 267 }(i, entry) 268 } 269 go func() { 270 wg.Wait() 271 close(errC) 272 }() 273 select { 274 case err = <-errC: 275 return err 276 case <-quitC: 277 return fmt.Errorf("aborted") 278 } 279 } 280 281 func retrieveToFile(quitC chan bool, fileStore *storage.FileStore, addr storage.Address, path string) error { 282 f, err := os.Create(path) // 283 if err != nil { 284 return err 285 } 286 reader, _ := fileStore.Retrieve(context.TODO(), addr) 287 writer := bufio.NewWriter(f) 288 size, err := reader.Size(context.TODO(), quitC) 289 if err != nil { 290 return err 291 } 292 if _, err = io.CopyN(writer, reader, size); err != nil { 293 return err 294 } 295 if err := writer.Flush(); err != nil { 296 return err 297 } 298 return f.Close() 299 }