webdavfs

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 30, 2026 License: MIT Imports: 9 Imported by: 0

README

afero-webdav

Go Reference

An afero filesystem implementation backed by a WebDAV server.

Usage

import (
    "github.com/isayme/afero-webdav"
    "github.com/studio-b12/gowebdav"
)

func main() {
    client := gowebdav.NewClient("https://webdav.example.com/dav/", "user", "password")
    fs := webdavfs.New(client)

    file, _ := fs.Create("/hello.txt")
    file.Write([]byte("Hello, WebDAV!"))
    file.Close()
}

Features

  • Create, read, write, and delete files
  • Make and remove directories (with MkdirAll/RemoveAll)
  • Rename/move files
  • Directory listing (Readdir, Readdirnames)
  • Range-based read with seek support (Seek, ReadAt)
  • Streaming writes via io.Pipe

Limitations

  • O_RDWR, O_APPEND, and O_EXCL flags are not supported
  • Write seeking is not supported
  • Chmod, Chtimes, Chown are not supported
  • Truncate is not supported

Documentation

Overview

Package webdavfs provides an afero filesystem implementation backed by a WebDAV server.

It wraps a gowebdav.Client and exposes a standard afero.Fs interface. All WebDAV protocol details (PROPFIND, GET, PUT, MKCOL, DELETE, MOVE) are handled by the underlying client. File reads use HTTP Range requests for efficient seeking; writes use io.Pipe for streaming uploads.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotSupported is returned when an operation cannot be performed over
	// the WebDAV protocol (e.g. Chmod, random-access writes).
	ErrNotSupported = errors.New("operation not supported")
	// ErrInvalidSeek is returned when a seek position would go negative.
	ErrInvalidSeek = errors.New("invalid seek offset")
	// ErrReadOnly is returned when Write is called on a file opened read-only.
	ErrReadOnly = errors.New("file opened read-only")
	// ErrWriteOnly is returned when Read is called on a file opened write-only.
	ErrWriteOnly = errors.New("file opened write-only")
	// ErrFileClosed is returned when an operation is attempted on a closed file.
	ErrFileClosed = errors.New("file is closed")
	// ErrIsDirectory is returned when a file operation is attempted on a directory.
	ErrIsDirectory = errors.New("is a directory")
	// ErrNotImplemented is a placeholder for operations that may be added later.
	ErrNotImplemented = errors.New("not implemented")
)

Sentinel errors for well-known failure modes of the WebDAV filesystem.

Functions

This section is empty.

Types

type File

type File struct {
	// contains filtered or unexported fields
}

File implements afero.File backed by WebDAV.

A File is in one of three states: read-only, write-only, or a directory handle. The state is determined by how the file was opened (see Fs.OpenFile).

Read state uses an HTTP GET stream with Range support for seeking. Write state uses an io.Pipe goroutine that uploads via PUT on Close. Directory state delegates listing to the WebDAV PROPFIND endpoint.

func NewFile

func NewFile(fs *Fs, name string) *File

func (*File) Close

func (f *File) Close() error

Close finalises the file handle.

For a read file: closes the underlying HTTP response body. For a write file: closes the PipeWriter, then waits for the background PUT goroutine to finish and reports any upload error. A closed directory handle is a no-op.

func (*File) Name

func (f *File) Name() string

func (*File) Read

func (f *File) Read(p []byte) (int, error)

Read reads up to len(p) bytes from the file.

The underlying HTTP GET stream is lazily opened on the first Read call. Subsequent reads continue streaming from where the previous read left off. When the cached file size is known, ReadStreamRange is used so that only the needed bytes are transferred.

func (*File) ReadAt

func (f *File) ReadAt(p []byte, off int64) (int, error)

ReadAt reads len(p) bytes starting at byte offset off.

Implemented as Seek + Read. This is not zero-copy but keeps the implementation simple and correct.

func (*File) Readdir

func (f *File) Readdir(count int) ([]os.FileInfo, error)

Readdir reads the directory contents via WebDAV PROPFIND with depth 1.

If count <= 0, all entries are returned. If count > 0, at most count entries are returned (partial read), matching the afero.File interface contract.

func (*File) Readdirnames

func (f *File) Readdirnames(n int) ([]string, error)

func (*File) Seek

func (f *File) Seek(offset int64, whence int) (int64, error)

Seek sets the read offset for the next Read or ReadAt call.

Write seeking is not supported because WebDAV PUT does not support partial updates (it always replaces the entire resource). Only read seeking is implemented, using HTTP Range requests.

When the current reader is at a different position, it is closed and a new stream will be opened lazily on the next Read.

func (*File) Stat

func (f *File) Stat() (os.FileInfo, error)

Stat returns file metadata. The result is cached in fileInfo for Seek(SeekEnd).

func (*File) Sync

func (f *File) Sync() error

Sync is a no-op because each Write call is already streaming to the server.

func (*File) Truncate

func (f *File) Truncate(_ int64) error

Truncate is not supported because WebDAV does not provide a truncation operation.

func (*File) Write

func (f *File) Write(p []byte) (int, error)

Write writes data to the file via the streaming PUT pipe.

Data is buffered in the io.Pipe and sent to the server in a background goroutine. The upload is finalised when Close() is called.

func (*File) WriteAt

func (f *File) WriteAt(p []byte, _ int64) (int, error)

WriteAt is not supported because WebDAV PUT always replaces the entire resource.

func (*File) WriteString

func (f *File) WriteString(s string) (int, error)

WriteString is a convenience wrapper around Write.

type Fs

type Fs struct {
	// contains filtered or unexported fields
}

Fs implements afero.Fs backed by a WebDAV server.

All operations are delegated to a gowebdav.Client which handles the WebDAV protocol (PROPFIND, GET, PUT, MKCOL, DELETE, MOVE) and authentication.

func New

func New(client *gowebdav.Client) *Fs

New creates a new WebDAV-backed filesystem from a pre-configured gowebdav.Client.

The caller is responsible for setting up authentication, timeouts, and transport on the client before passing it here.

func (*Fs) Chmod

func (fs *Fs) Chmod(_ string, _ os.FileMode) error

Chmod is not supported because standard WebDAV does not expose Unix permission bits.

func (*Fs) Chown

func (fs *Fs) Chown(_ string, _, _ int) error

Chown is not supported because WebDAV does not expose ownership metadata.

func (*Fs) Chtimes

func (fs *Fs) Chtimes(_ string, _, _ time.Time) error

Chtimes is not supported because WebDAV does not provide a standard method for setting modification times independently of upload.

func (*Fs) Create

func (fs *Fs) Create(name string) (afero.File, error)

Create creates a new file and returns a writable handle.

An empty object is written first to ensure the file exists on the server, then the file is re-opened for streaming write. This two-step approach mirrors afero-s3 (PutObject then OpenFile) and works around the lack of an atomic "create-or-truncate" in the WebDAV PUT semantics.

func (*Fs) Mkdir

func (fs *Fs) Mkdir(name string, perm os.FileMode) error

func (*Fs) MkdirAll

func (fs *Fs) MkdirAll(name string, perm os.FileMode) error

func (*Fs) Name

func (fs *Fs) Name() string

func (*Fs) Open

func (fs *Fs) Open(name string) (afero.File, error)

func (*Fs) OpenFile

func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error)

OpenFile opens a file for reading or writing.

Supported flags: O_RDONLY, O_WRONLY, O_CREATE (implied by O_WRONLY).

O_RDWR, O_APPEND, and O_EXCL are not supported because WebDAV operates over HTTP which does not provide the necessary atomic read-write or append guarantees.

For reads: the file is stat'd first to cache its size (needed for Range-based seeking). The actual HTTP GET request is deferred to the first Read() call.

For writes: a streaming upload via io.Pipe is started immediately. The caller must Close() the file to finalise the upload.

func (*Fs) Remove

func (fs *Fs) Remove(name string) error

Remove deletes a single file or empty directory via WebDAV DELETE.

func (*Fs) RemoveAll

func (fs *Fs) RemoveAll(name string) error

RemoveAll recursively deletes a path and all its children.

gowebdav.Client.RemoveAll handles the recursive listing and deletion.

func (*Fs) Rename

func (fs *Fs) Rename(oldname, newname string) error

Rename moves a file or directory via WebDAV MOVE.

Identity rename (oldname == newname) is a no-op. Overwrite is disabled (gowebdav Rename with overwrite=false).

func (*Fs) Stat

func (fs *Fs) Stat(name string) (os.FileInfo, error)

Stat returns file or directory metadata via WebDAV PROPFIND.

The root path "/" is handled locally because many WebDAV servers return inconsistent results for a PROPFIND on "/".

type PathError

type PathError struct {
	Op   string
	Path string
	Err  error
}

PathError records an error and the operation and path that caused it, analogous to os.PathError.

func (*PathError) Error

func (e *PathError) Error() string

func (*PathError) Unwrap

func (e *PathError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL