Skip to content

Latest commit

History

291 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation


Contents


Install

go get github.com/ilyabrin/disk

Note

Requires Go 1.23 or newer. The library has no runtime dependencies beyond the standard library.

Quick start

package main
import (
"context""fmt""log""github.com/ilyabrin/disk"
)
funcmain() {
// Reads YANDEX_DISK_ACCESS_TOKEN when no token is passed explicitly.client, err:=disk.New()
iferr!=nil {
log.Fatal(err)
}
ctx:=context.Background()
info, err:=client.DiskInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Used %s of %s\n",
disk.FormatFileSize(int64(info.UsedSpace)),
disk.FormatFileSize(int64(info.TotalSpace)),
)
}

Authentication

The client authenticates with an OAuth token. Get one from the Yandex OAuth console with the cloud_api:disk.* scopes.

client, err:=disk.New("y0_AgAAAA...") // explicit tokenclient, err:=disk.New() // from $YANDEX_DISK_ACCESS_TOKEN

Warning

Never commit tokens. The built-in logger redacts Authorization headers, but your own logging is your responsibility.

Disk info

info, err:=client.DiskInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Println("Total:", info.TotalSpace)
fmt.Println("Used:", info.UsedSpace)
fmt.Println("Trash:", info.TrashSize)
fmt.Println("Downloads folder:", info.SystemFolders.Downloads)

Files and folders

Metadata
resource, errResp:=client.GetMetadata(ctx, "/Documents/report.pdf")
iferrResp!=nil {
log.Fatal(errResp.Error)
}
fmt.Println(resource.Name, resource.Size, resource.MimeType)

Listing a folder returns its contents in _embedded. Use GetMetadataWithOptions to paginate, sort, and trim the response:

folder, errResp:=client.GetMetadataWithOptions(ctx, "/Photos", &disk.ResourceOptions{
Limit: 100,
Offset: 0,
Sort: "-modified", // "-" reverses the orderPreviewSize: "M",
PreviewCrop: true,
Fields: []string{"name", "_embedded.items.name", "_embedded.items.size"},
})
for_, item:=rangefolder.Embedded.Items {
fmt.Println(item.Type, item.Name)
}
Create, copy, move, delete
// Create a folderlink, errResp:=client.CreateDir(ctx, "/Reports")
// Copylink, errResp=client.CopyResource(ctx, "/a.txt", "/backup/a.txt")
// Move or renamelink, errResp=client.MoveResource(ctx, "/a.txt", "/archive/a-2026.txt")
// Delete (to trash)err:=client.DeleteResource(ctx, "/a.txt", false)
// Delete permanentlyerr=client.DeleteResource(ctx, "/a.txt", true)
Custom properties
resource, errResp:=client.UpdateMetadata(ctx, "/report.pdf",
map[string]map[string]string{
"custom_properties": {
"project": "apollo",
"status": "final",
},
})
Flat file list and recent uploads
// All files, newest first, images and video onlyfiles, errResp:=client.GetSortedFilesWithOptions(ctx,
&disk.PaginationOptions{Limit: 50},
&disk.FilesOptions{
MediaType: []string{"image", "video"},
Sort: "-created",
})
// Last uploaded resourcesrecent, errResp:=client.GetLastUploadedResources(ctx)

Upload

MethodUse for
UploadFileFromPathAny local file, with full option control
UploadFileFromPathWithProgressSmall/medium files with a progress bar
UploadLargeFileFromPathLarge files, chunked progress reporting
UploadFileServer-side fetch: Yandex downloads a URL for you
resource, err:=client.UploadFileFromPath(ctx, "./report.pdf", "/Documents/report.pdf",
&disk.UploadOptions{Overwrite: true})

With progress:

resource, err:=client.UploadFileFromPathWithProgress(ctx,
"./video.mp4", "/Videos/video.mp4", true,
func(p disk.UploadProgress) {
fmt.Printf("\r%.1f%% (%s / %s)",
p.Percentage,
disk.FormatFileSize(p.BytesUploaded),
disk.FormatFileSize(p.TotalBytes),
)
})

Large files, reporting once per 10 MB chunk:

resource, err:=client.UploadLargeFileFromPath(ctx,
"./archive.zip", "/Backups/archive.zip", 10,
func(p disk.UploadProgress) {
log.Printf("uploaded %s", disk.FormatFileSize(p.BytesUploaded))
})

Let Yandex fetch a remote URL directly, without routing bytes through your process:

link, errResp:=client.UploadFile(ctx, "/Downloads/image.jpg", "https://example.com/image.jpg")

Tip

UploadFile is asynchronous — poll the returned link with GetOperationStatus to find out when the file has landed.

Download

err:=client.DownloadFileToPath(ctx, "/Photos/image.jpg", "./image.jpg",
&disk.DownloadOptions{Overwrite: true})

With progress:

err:=client.DownloadFileToPathWithProgress(ctx,
"/Videos/video.mp4", "./video.mp4", true,
func(p disk.DownloadProgress) {
ifp.TotalBytes>0 {
fmt.Printf("\r%.1f%%", p.Percentage)
}
})

Need the raw link instead (for a CDN, a browser redirect, or your own transfer code)?

link, errResp:=client.GetDownloadURL(ctx, "/Photos/image.jpg")
fmt.Println(link.Href) // short-lived, single-use

Public resources

// Publish and unpublishlink, errResp:=client.PublishResource(ctx, "/Photos/image.jpg")
link, errResp=client.UnpublishResource(ctx, "/Photos/image.jpg")
// Everything you have publishedlist, errResp:=client.GetPublicResources(ctx)

Reading someone else's published resource by key or URL:

resource, errResp:=client.GetMetadataForPublicResource(ctx, "https://yadi.sk/d/abc123")
// Browse inside a published folderresource, errResp=client.GetMetadataForPublicResourceWithOptions(ctx, "https://yadi.sk/d/abc123",
&disk.PublicResourceOptions{
Path: "/subfolder",
Sort: "name",
Limit: 50,
})
// Download a specific file from a published folderlink, errResp:=client.GetDownloadURLForPublicResourceAt(ctx, "https://yadi.sk/d/abc123", "/subfolder/file.txt")
// Save it into your own Downloads folder under a new namelink, errResp=client.SavePublicResourceWithOptions(ctx, "https://yadi.sk/d/abc123",
&disk.SavePublicResourceOptions{Path: "/subfolder/file.txt", Name: "copy.txt"})

Trash

// Browsetrash, err:=client.ListTrashResources(ctx, "", 100, 0)
// Metadata for one itemitem, err:=client.GetTrashResourceMetadata(ctx, "report.pdf", nil)
// Restore, optionally renaminglink, err:=client.RestoreFromTrash(ctx, "report.pdf", false, "report-restored.pdf")
// Empty a single path, or the whole trash with ""err=client.EmptyTrash(ctx, "", false)

Note

Pass forceAsync: true to EmptyTrash to make the API always answer 202 with an operation link instead of blocking on a large deletion.

Pagination

Three styles are available, from lowest to highest level. See PAGINATION.md for the full guide.

1. Explicit limit/offset
files, errResp:=client.GetSortedFilesWithPagination(ctx, &disk.PaginationOptions{
Limit: 50,
Offset: 100,
})
2. Paged results with metadata
page, errResp:=client.GetSortedFilesPaged(ctx, &disk.PaginationOptions{Limit: 50})
fmt.Println(page.Pagination.HasMore, page.Pagination.NextOffset)
3. Iterator
it:=client.GetSortedFilesIterator(&disk.PaginationOptions{Limit: 100})
forit.HasNext() {
page, err:=it.Next(ctx)
iferr!=nil {
log.Fatal(err)
}
for_, file:=rangepage.Items {
fmt.Println(file.Name)
}
}

Batch operations

Batch helpers run operations concurrently and collect per-item results instead of failing on the first error.

status, err:=client.BatchDeleteFiles(ctx,
[]string{"/tmp/a.txt", "/tmp/b.txt", "/tmp/c.txt"},
&disk.BatchDeleteOptions{
BatchOptions: disk.BatchOptions{
MaxConcurrency: 4,
ContinueOnError: true,
Progress: func(s disk.BatchOperationStatus) {
fmt.Printf("\r%d/%d", s.Completed, s.Total)
},
},
Permanently: false,
})
fmt.Println(status.GetSummary())
for_, failure:=rangestatus.GetFailedOperations() {
log.Printf("%s: %v", failure.Path, failure.Error)
}
// Retry only what failedstatus, err=client.RetryFailedOperations(ctx, status, 2)

Available: BatchDeleteFiles, BatchCopyFiles, BatchMoveFiles, BatchUpdateMetadata, plus the convenience wrappers BatchRenameFiles, BatchMoveToDirectory, BatchCopyToDirectory and the *Simple variants.

Configuration

client, err:=disk.NewWithConfig(&disk.ClientConfig{
DefaultTimeout: 60*time.Second,
MaxRetries: 3,
RetryBackoff: 200*time.Millisecond,
EnableDebugLogging: true,
Logger: disk.DefaultLoggerConfig(),
}, "your-token")
FieldDefaultMeaning
DefaultTimeout30sPer-request timeout when the context carries no deadline
MaxRetries3Extra attempts for retryable requests
RetryBackoff200msBase delay between retries; doubles each attempt
EnableDebugLoggingfalseSwitches the logger to DEBUG and verbose mode
Loggersee belowLogger configuration
BaseURLYandex APIOverride the endpoint (tests, proxies)

Important

Only requests without a body are retried — GET, DELETE, and the PUT/POST calls that carry their parameters in the query string. A request whose body is an io.Reader cannot be rewound, so it is sent exactly once. Retries fire on connection errors, 429, and 5xx.

Timeouts can also be set per call through the context:

ctx, cancel:=disk.WithTimeout(10*time.Second)
defercancel()
info, err:=client.DiskInfo(ctx)

Logging

client.SetLogLevel(disk.DEBUG) // DEBUG, INFO, WARN, ERROR, SILENTclient.SetVerbose(true) // include request/response detailsclient.SetLogOutput(os.Stderr) // any io.Writer

Sensitive header values (Authorization, tokens) are redacted before they reach the log output.

Error handling

The library uses two error conventions, and which one you get depends on the call:

Return typeWhereHow to handle
*ErrorResponseResource, public and pagination callsNon-nil means failure; read .Error and .Description
errorDisk info, upload, download, trash, batchStandard Go handling, wrapped with %w
resource, errResp:=client.GetMetadata(ctx, "/missing.txt")
iferrResp!=nil {
log.Printf("%s: %s", errResp.Error, errResp.Description)
return
}
iferr:=client.DownloadFileToPath(ctx, "/a.txt", "./a.txt", nil); err!=nil {
log.Fatal(err)
}

Async operations

Copy, move, save-to-disk and trash operations may answer 202 Accepted with a link to a background operation. Poll it until it reports success:

link, errResp:=client.CopyResource(ctx, "/big-folder", "/backup/big-folder")
iferrResp!=nil {
log.Fatal(errResp.Error)
}
for {
// Accepts either an operation ID or the full href from the response.operation, err:=client.GetOperationStatus(ctx, link.Href)
iferr!=nil {
log.Fatal(err)
}
ifoperation.Status!=disk.OperationInProgress {
fmt.Println("finished:", operation.Status)
break
}
time.Sleep(time.Second)
}

For batch calls, WaitForBatchOperation does the polling for you:

err:=client.WaitForBatchOperation(ctx, status, time.Second)

Examples

Runnable programs live in examples/:

ExampleShows
demoDisk info, metadata, folder and file operations
uploadUploads with progress reporting
paginationAll three pagination styles
export YANDEX_DISK_ACCESS_TOKEN=your-token
go run ./examples/demo

Development

go test ./... # run the suite
go test -race -cover ./... # with the race detector
go vet ./... # static checks
golangci-lint run # full lint (see .golangci.yml)

CI runs tests, go vet, CodeQL, govulncheck and gosec on every push and pull request; gosec findings are published as code scanning alerts.

Contributions are welcome — open an issue or a pull request.

License

MIT © Ilya Brin

Releases

Packages

Used by

Contributors

Languages