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