github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/file/doc.go (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache-2.0 3 // license that can be found in the LICENSE file. 4 5 // Package file provides basic file operations across multiple file-system 6 // types. It is designed for use in applications that operate uniformly on 7 // multiple storage types, such as local files, S3 and HTTP. 8 // 9 // Overview 10 // 11 // This package is designed with following goals: 12 // 13 // - Support popular file systems, especially S3 and the local file system. 14 // 15 // - Define operation semantics that are implementable on all the supported file 16 // systems, yet practical and usable. 17 // 18 // - Extensible. Provide leeway to do things like registering new file system 19 // types or ticket-based authorizations. 20 // 21 // This package defines two key interfaces, Implementation and File. 22 // 23 // - Implementation provides filesystem operations, such as Open, Remove, and List 24 // (directory walking). 25 // 26 // - File implements operations on a file. It is created by 27 // Implementation.{Open,Create} calls. File is similar to go's os.File object 28 // but provides limited functionality. 29 // 30 // Reading and writing files 31 // 32 // The following snippet shows registering an S3 implementation, then writing 33 // and reading a S3 file. 34 // 35 // import ( 36 // "context" 37 // "ioutil" 38 // 39 // "github.com/Schaudge/grailbase/file" 40 // "github.com/Schaudge/grailbase/file/s3file" // file.Implementation implementation for S3 41 // "github.com/aws/aws-sdk-go/aws/session" 42 // "github.com/stretchr/testify/require" 43 // ) 44 // 45 // func init() { 46 // file.RegisterImplementation("s3", s3file.NewImplementation( 47 // s3file.NewDefaultProvider())) 48 // } 49 // 50 // // Caution: this code ignores all errors. 51 // func WriteTest() { 52 // ctx := context.Background() 53 // f, err := file.Create(ctx, "s3://grail-saito/tmp/test.txt") 54 // n, err = f.Writer(ctx).Write([]byte{"Hello"}) 55 // err = f.Close(ctx) 56 // } 57 // 58 // func ReadTest() { 59 // ctx := context.Background() 60 // f, err := file.Open(ctx, "s3://grail-saito/tmp/test.txt") 61 // data, err := ioutil.ReadAll(f.Reader(ctx)) 62 // err = f.Close(ctx) 63 // } 64 // 65 // To open a file for reading or writing, run file.Open("s3://bucket/key") or 66 // file.Create("s3://bucket/key"). A File object does not implement an io.Reader 67 // or io.Writer directly. Instead, you must call File.Reader or File.Writer to 68 // start reading or writing. These methods are split from the File itself so 69 // that an application can pass different contexts to different I/O operations. 70 // 71 // File-system operations 72 // 73 // The file package provides functions similar to those in the standard os 74 // class. For example, file.Remove("s3://bucket/key") removes a file, and 75 // file.Stat("s3://bucket/key") provides a metadata about the file. 76 // 77 // Pathname utility functions 78 // 79 // The file package also provides functions that are similar to those in the 80 // standard filepath package. Functions file.Base, file.Dir, file.Join work just 81 // like filepath.{Base,Dir,Join}, except that they handle the URL pathnames 82 // properly. For example, file.Join("s3://foo", "bar") will return 83 // "s3://foo/bar", whereas filepath.Join("s3://foo", "bar") would return 84 // "s3:/foo/bar". 85 // 86 // Registering a filesystem implementation 87 // 88 // Function RegisterImplementation associates an implementation to a scheme 89 // ("s3", "http", "git", etc). A local file system implementation is 90 // automatically available without any explicit 91 // registration. RegisterImplementation is usually invoked when a process starts 92 // up, for all the supported file system types. For example: 93 // 94 // import ( 95 // "ioutil" 96 // "github.com/Schaudge/grailbase/context" 97 // "github.com/Schaudge/grailbase/file" 98 // "github.com/Schaudge/grailbase/file/s3file" // file.Implementation implementation for S3 99 // ) 100 // func init() { 101 // file.RegisterImplementation("s3:", s3file.NewImplementation(...)) 102 // } 103 // func main() { 104 // ctx := context.Background() 105 // f, err := file.Open(ctx, "s3://somebucket/foo.txt") 106 // data, err := ioutil.ReadAll(f.Reader(ctx)) 107 // err := f.Close(ctx) 108 // ... 109 // } 110 // 111 // Once an implementation is registered, the files for that scheme can be opened 112 // or created using "scheme:name" pathname. 113 // 114 // Differences from the os package 115 // 116 // The file package is similar to Go's standard os package. The differences are 117 // the following. 118 // 119 // - The file package focuses on providing a file-like API for object storage 120 // systems, such as S3 or GCS. 121 // 122 // - Mutations to a File are restricted to whole-file writes. There is no option 123 // to overwrite a part of an existing file. 124 // 125 // - All the operations take a context parameter. 126 // 127 // - file.File does not implement io.Reader nor io.Writer directly. One must 128 // call File.Reader or File.Writer methods to obtains a reader or writer object. 129 // 130 // - Directories are simulated in a best-effort manner on implementations that do 131 // not support directories as first-class entities, such as S3. Lister provides 132 // IsDir() for the current path. Info(path) returns nil for directories. 133 // 134 // Concurrency 135 // 136 // The Implementation and File provide an open-close consistency. More 137 // specifically, this package linearizes fileops, with a fileop defined in the 138 // following way: fileop is a set of operations, starting from 139 // Implementation.{Open,Create}, followed by read/write/stat operations on the 140 // file, followed by File.Close. Operations such as 141 // Implementation.{Stat,Remove,List} and Lister.Scan form a singleton fileop. 142 // 143 // Caution: a local file system on NFS (w/o cache leasing) doesn't provide this 144 // guarantee. Use NFS at your own risk. 145 package file