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 }