wfs とは
jarxorg/wfs で開発している Go標準 の io/fs を拡張したライブラリです。 現在は以下のファイルシステムをサポートしています。
- osfs
- memfs
- s3fs
- gcsfs
開発の動機
Go で S3 や GCS を使うほとんどのプロジェクトで、クラウドストレージにアクセスするためのインタフェースを新しく定義し、開発用にローカルファイルシステムの実装、テスト用にメモリ上での実装をして切り替えれるようなコードを書いてきました。
Go1.16 から登場した io/fs は今のところ読み取り専用で、書き込みについては proposal: io/fs: add writable interfaces などで議論されていますが簡単には進まなそうです。
その Issue でも紹介されている afero や billy は非常に優秀ですが S3 に対応していなかったりして微妙に私のユースケースにマッチしません。
そこで io/fs を拡張した wfs を開発することにしました。
書き込みサンプル
package main
import (
"fmt"
"io/fs"
"log"
"os"
"strings"
"github.com/jarxorg/gcsfs"
"github.com/jarxorg/s3fs"
"github.com/jarxorg/wfs"
"github.com/jarxorg/wfs/memfs"
"github.com/jarxorg/wfs/osfs"
)
func newFS(dir string) fs.FS {
if strings.HasPrefix(dir, "s3://") {
return s3fs.New(strings.TrimLeft(dir, "s3://"))
}
if strings.HasPrefix(dir, "gs://") {
return gcsfs.New(strings.TrimLeft(dir, "s3://"))
}
if strings.HasPrefix(dir, "memfs://") {
return memfs.New()
}
return osfs.New(dir)
}
func main() {
dir := "memfs://dummy"
if len(os.Args) > 1 {
dir = os.Args[1]
}
name := "example.txt"
content := []byte(`Hello`)
fsys := newFS(dir)
_, err := wfs.WriteFile(fsys, name, content, fs.ModePerm)
if err != nil {
log.Fatal(err)
}
wrote, err := wfs.ReadFile(fsys, name)
if err != nil {
log.Fatal(err)
}
fmt.Printf("wrote: %s/%s\n\n%s\n", dir, name, string(wrote))
}
CopyFS サンプル
書き込めるようになったので、便利な CopyFS 関数という副産物も生まれました。
package main
import (
"log"
"github.com/jarxorg/s3fs"
"github.com/jarxorg/wfs"
"github.com/jarxorg/wfs/osfs"
)
func main() {
src := s3fs.New("your-bucket")
dst := osfs.DirFS("local-dir")
// NOTE: Copy files on s3://your-bucket to local-dir.
if err := wfs.CopyFS(dst, src, "."); err != nil {
log.Fatal(err)
}
}
上記は s3 バケットからローカルにコピーするサンプルですが、以下も同じようなコードで簡単にできます。
- gcs バケットから s3 にコピー
- ローカルからメモリにコピー
※ 並列実行などしていないのであまり巨大なバケットのコピーにはご注意ください。
S3 や GCS の認証
S3 ならば s3fs.NewWithAPI や s3fs.NewWithSession、GCS ならば gcsfs.NewWithClient を使うことができます。
最後に
いまのところ追加開発の予定はありませんが、バグなどあれば精力的になおしていくつもりです!