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