I know there are Go libraries that create entire filesystems like VFS. But I only want to make a byte array into something that can fulfill the File interface.
There is no ready solution for this in the standard library, but it's not that hard to do it yourself.
What we need is this http.File
interface:
type File interface {
io.Closer
io.Reader
io.Seeker
Readdir(count int) ([]os.FileInfo, error)
Stat() (os.FileInfo, error)
}
Please note that we can utilize bytes.Reader
to do the heavy task, as that alone implements io.Reader
and io.Seeker
. io.Closer
can be a noop, and Readdir()
may return nil, nil
as we're mocking a file not a directory, its Readdir()
won't even be called.
The "hardest" part is to mock Stat()
to return a value that implements os.FileInfo
.
Here's a simple mocked FileInfo
:
type myFileInfo struct {
name string
data []byte
}
func (mif myFileInfo) Name() string { return mif.name }
func (mif myFileInfo) Size() int64 { return int64(len(mif.data)) }
func (mif myFileInfo) Mode() os.FileMode { return 0444 } // Read for all
func (mif myFileInfo) ModTime() time.Time { return time.Time{} } // Return anything
func (mif myFileInfo) IsDir() bool { return false }
func (mif myFileInfo) Sys() interface{} { return nil }
And with that we have everything to create our mocked http.File
:
type MyFile struct {
*bytes.Reader
mif myFileInfo
}
func (mf *MyFile) Close() error { return nil } // Noop, nothing to do
func (mf *MyFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil // We are not a directory but a single file
}
func (mf *MyFile) Stat() (os.FileInfo, error) {
return mf.mif, nil
}
Example using it (try it on the Go Playground):
data := []byte{0, 1, 2, 3}
mf := &MyFile{
Reader: bytes.NewReader(data),
mif: myFileInfo{
name: "somename.txt",
data: data,
},
}
var f http.File = mf
_ = f