github.com/viant/toolbox@v0.34.5/storage/file_service.go (about)

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/pkg/errors"
     6  	"github.com/viant/toolbox"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/url"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  )
    14  
    15  var fileMode os.FileMode = 0644
    16  var execFileMode os.FileMode = 0755
    17  
    18  //Service represents abstract way to accessing local or remote storage
    19  type fileStorageService struct{}
    20  
    21  //List returns a list of object for supplied url
    22  func (s *fileStorageService) List(URL string) ([]Object, error) {
    23  	file, err := toolbox.OpenFile(URL)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	defer file.Close()
    28  
    29  	stat, err := file.Stat()
    30  	if err == nil {
    31  		if !stat.IsDir() {
    32  			return []Object{
    33  				newFileObject(URL, stat),
    34  			}, nil
    35  		}
    36  	}
    37  	files, err := file.Readdir(0)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	var result = make([]Object, 0)
    42  	result = append(result, newFileObject(URL, stat))
    43  
    44  	var parsedURL, _ = url.Parse(URL)
    45  	for _, fileInfo := range files {
    46  		var fileName = fileInfo.Name()
    47  		if parsedURL != nil {
    48  			fileName = strings.Replace(fileName, parsedURL.Path, "", 1)
    49  		}
    50  
    51  		fileURL := toolbox.URLPathJoin(URL, fileName)
    52  		result = append(result, newFileObject(fileURL, fileInfo))
    53  	}
    54  	return result, nil
    55  }
    56  
    57  //Exists returns true if resource exists
    58  func (s *fileStorageService) Exists(URL string) (bool, error) {
    59  	parsedUrl, err := url.Parse(URL)
    60  	if err != nil {
    61  		return false, err
    62  	}
    63  	if parsedUrl.Scheme != "file" {
    64  		return false, fmt.Errorf("invalid schema, expected file but had: %v", parsedUrl.Scheme)
    65  	}
    66  	return toolbox.FileExists(parsedUrl.Path), nil
    67  }
    68  
    69  func (s *fileStorageService) Close() error {
    70  	return nil
    71  }
    72  
    73  //Object returns a Object for supplied url
    74  func (s *fileStorageService) StorageObject(URL string) (Object, error) {
    75  	file, err := toolbox.OpenFile(URL)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	defer file.Close()
    80  	fileInfo, err := os.Stat(file.Name())
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return newFileObject(URL, fileInfo), nil
    85  }
    86  
    87  //Download returns reader for downloaded storage object
    88  func (s *fileStorageService) Download(object Object) (io.ReadCloser, error) {
    89  	return toolbox.OpenFile(object.URL())
    90  }
    91  
    92  //DownloadWithURL downloads content for passed in object URL
    93  func (s *fileStorageService) DownloadWithURL(URL string) (io.ReadCloser, error) {
    94  	object, err := s.StorageObject(URL)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	return s.Download(object)
    99  }
   100  
   101  func (s *fileStorageService) Upload(URL string, reader io.Reader) error {
   102  	return s.UploadWithMode(URL, DefaultFileMode, reader)
   103  }
   104  
   105  //Upload uploads provided reader content for supplied url.
   106  func (s *fileStorageService) UploadWithMode(URL string, mode os.FileMode, reader io.Reader) error {
   107  	if mode == 0 {
   108  		mode = DefaultFileMode
   109  	}
   110  	parsedUrl, err := url.Parse(URL)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if parsedUrl.Scheme != "file" {
   115  		return fmt.Errorf("Invalid schema, expected file but had: %v", parsedUrl.Scheme)
   116  	}
   117  
   118  	parentDir, _ := path.Split(parsedUrl.Path)
   119  
   120  	err = toolbox.CreateDirIfNotExist(parentDir)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	data, err := ioutil.ReadAll(reader)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	return ioutil.WriteFile(parsedUrl.Path, data, mode)
   129  }
   130  
   131  func (s *fileStorageService) Register(schema string, service Service) error {
   132  	return errors.New("unsupported")
   133  }
   134  
   135  //Delete removes passed in storage object
   136  func (s *fileStorageService) Delete(object Object) error {
   137  
   138  	if object.IsFolder() {
   139  		objects, err := s.List(object.URL())
   140  		if err != nil {
   141  			return err
   142  		}
   143  		for _, listedObject := range objects {
   144  			if listedObject.URL() == object.URL() {
   145  				continue
   146  			}
   147  			if err := s.Delete(listedObject); err != nil {
   148  				return err
   149  			}
   150  		}
   151  
   152  	}
   153  	fileName := toolbox.Filename(object.URL())
   154  	return os.Remove(fileName)
   155  }
   156  
   157  type fileStorageObject struct {
   158  	*AbstractObject
   159  }
   160  
   161  func NewFileStorage() Service {
   162  	return &fileStorageService{}
   163  }
   164  
   165  func (o *fileStorageObject) Unwrap(target interface{}) error {
   166  	if fileInfo, casted := target.(*os.FileInfo); casted {
   167  		source, ok := o.Source.(os.FileInfo)
   168  		if !ok {
   169  			return fmt.Errorf("failed to cast %T into %T", o.Source, target)
   170  		}
   171  		*fileInfo = source
   172  		return nil
   173  	}
   174  
   175  	return fmt.Errorf("unsuported target %T", target)
   176  }
   177  
   178  func (o *fileStorageObject) FileInfo() os.FileInfo {
   179  	if source, ok := o.Source.(os.FileInfo); ok {
   180  		return source
   181  	}
   182  	return nil
   183  }
   184  
   185  func newFileObject(url string, fileInfo os.FileInfo) Object {
   186  	abstract := NewAbstractStorageObject(url, fileInfo, fileInfo)
   187  	result := &fileStorageObject{
   188  		AbstractObject: abstract,
   189  	}
   190  	result.AbstractObject.Object = result
   191  	return result
   192  }
   193  
   194  const FileProviderSchema = "file"
   195  
   196  func init() {
   197  	Registry().Registry[FileProviderSchema] = fileServiceProvider
   198  
   199  }
   200  
   201  func fileServiceProvider(credentialFile string) (service Service, err error) {
   202  	return NewFileStorage(), nil
   203  }