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  }