Available since: v3.0.0
File functions
These functions can be used to work with files.
Changes to files in a bucket are not part of a transaction. file::put(), file::delete(), file::rename(), file::copy() and their _if_not_exists forms take effect immediately, so the rest of the query can see the change. If the transaction is then cancelled by CANCEL, THROW or a failed statement, the database changes are rolled back but the file changes stay. The response for the file statement still reports The query was not executed due to a cancelled transaction. Change files after the transaction has committed, or undo the file changes in your error handling.
Function | Description |
|---|---|
file::bucket() | Returns the bucket path from a file pointer |
file::copy() | Copies the contents of a file |
file::copy_if_not_exists() | Copies the contents of a file to a new file if the name is available |
file::delete() | Deletes a file |
file::exists() | Checks if a file already exists |
file::get() | Loads a file |
file::head() | Returns the metadata of a file |
file::key() | Returns the key (the portion following the bucket) from a file pointer |
file::list() | Returns a list of files inside a bucket |
file::presign_get() | Returns a signed URL that downloads a file without a session |
file::presign_put() | Returns a signed URL that uploads a file without a session |
file::put() | Writes bytes to a file |
file::put_if_not_exists() | Attempts to write bytes to a file |
file::rename() | Renames a file |
file::rename_if_not_exists() | Renames a file if the new name is not already in use |
file::bucket
file::bucket(file) -> stringThe file::bucket function returns the name of the bucket in which a file is located.
file::bucket(f"my_bucket:/file_name");
DEFINE PARAM $SOME_DATABASE_FILE VALUE f"my_bucket:/file_name";
file::bucket($SOME_DATABASE_FILE);'my_bucket'The counterpart to this function is file::key, which returns the latter part of a file pointer.
file::copy
The file::copy function copies the contents of a file to a new file, overwriting any existing file that has the same name as the new file.
file::copy(string)Example of a file my_book.txt being copied to a new location lion_witch_wardrobe.txt:
f"my_bucket:/my_book.txt".copy("lion_witch_wardrobe.txt"); file::copy_if_not_exists
The file::copy_if_not_exists function copies the contents of a file to a new file, returning an error if a file already exists that has the same name as that of the intended copy.
file::copy_if_not_exists(string)Example of a file my_book.txt attempting to copy to a new location lion_witch_wardrobe.txt:
DEFINE BUCKET my_bucket BACKEND "memory";
f"my_bucket:/lion_witch_wardrobe.txt".put("Once there were four children whose names were Peter, Susan...");
f"my_bucket:/other_book.txt".put("Um meine Geschichte zu erzählen, muß ich weit vorn anfangen.");
f"my_bucket:/other_book.txt".copy_if_not_exists("lion_witch_wardrobe.txt");'Operation for bucket `my_bucket` failed: Object at location lion_witch_wardrobe.txt already exists:
Object already exists at that location: lion_witch_wardrobe.txt' file::delete
The file::delete function deletes a file.
file::delete(string)Example of a file my_book.txt being deleted:
f"my_bucket:/my_book.txt".delete(); file::exists
The file::exists function checks to see if a file exists at the path and file name indicated.
file::exists(string) -> boolExample of an IF else STATEMENT used to check if a file exists before writing content to the location:
IF f"my_bucket:/my_book.txt".exists() {
THROW "Whoops, already there!"
} ELSE {
f"my_bucket:/my_book.txt".put("Some content")
}; file::get
The file::get function retrieves a file for use.
file::get(string) -> bytesA retrieved file will display as bytes. If valid text, these can be cast into a string.
f"my_bucket:/my_book.txt".get();
<string>f"my_bucket:/my_book.txt".get();-------- Query --------
b"536F6D6520636F6E74656E74"
-------- Query --------
'Once there were four children whose names were Peter, Susan...' file::head
The file::head function returns the metadata for a file.
file::head(file) -> option<object>If a file is found, the metadata will be returned as an object with the following fields:
file(file): the pointer to the file.size(int): the file size in bytes.updated(datetime): the last time a change was made to the file.etag(option<string>): an identifier that changes when the file changes, from SurrealDB 4.0. On afile:bucket it is a weak identifier that starts withW/, because file modification times on disk do not have the same precision on every system.content_type(option<string>): the media type stored with the file, present only when one was given tofile::put. From SurrealDB 4.0.
An example of this function and its output:
DEFINE BUCKET my_bucket BACKEND "memory";
f"my_bucket:/cover.png".put(<bytes>"PNG", { content_type: "image/png" });
f"my_bucket:/cover.png".head();{
content_type: 'image/png',
etag: '"0"',
file: f"my_bucket:/cover.png",
size: 3,
updated: d'2026-11-02T10:15:04.221917Z'
} file::key
file::key(file) -> stringThe file::key function returns the key of a file: the part of a file pointer following the bucket name.
file::key(f"my_bucket:/file_name");
DEFINE PARAM $SOME_DATABASE_FILE VALUE f"my_bucket:/file_name";
file::key($SOME_DATABASE_FILE);'/file_name'The counterpart to this function is file::bucket, which returns the bucket name of a file pointer.
file::list
file::list(string, $list_options: option<object>) -> array<object>The file::list function returns the metadata for the files inside a bucket, in key order. Each object in the output has the following fields:
file: the pointer to the file.size(int): the file size in bytes.updated(datetime): the last time a change was made to the file.etag(option<string>): the same identifier thatfile::headreturns.
A listing never contains content_type, because the listings of the cloud object storage services do not carry it. Use file::head to read the type of a file.
DEFINE BUCKET my_bucket BACKEND "memory";
f"my_bucket:/some_book".put("Once upon a time...");
f"my_bucket:/some_book".rename("awesome_book");
f"my_bucket:/some_book".put("In a hole in the ground lived a Hobbit.");
file::list("my_bucket");[
{
etag: '"1"',
file: f"my_bucket:/awesome_book",
size: 19,
updated: d'2026-11-02T03:27:10.360294Z'
},
{
etag: '"2"',
file: f"my_bucket:/some_book",
size: 39,
updated: d'2026-11-02T03:27:10.360458Z'
}
]A second argument holds options for the listing. It is an object with up to four fields:
prefix(string): returns only the files whose keys start with this string.delimiter(string): groups the files below the prefix by folder. The only accepted value is"/".start(stringorfile): returns only the entries after this key. The entry forstartitself is not returned.limit(int): the maximum number of entries to return.
From SurrealDB 4.0, these options have the same meaning on every backend. Before 4.0, a file: bucket listed one folder level and matched prefix by whole folder names, and a cloud object storage bucket also matched prefix by whole folder names, while a memory bucket matched it as a string.
Prefixes
prefix is a plain string prefix, as in Amazon S3, so it does not have to end at a /. A trailing / is kept, which limits the listing to the contents of that folder. The examples below use a bucket with six files.
DEFINE BUCKET docs BACKEND "memory";
FOR $key IN [
"/a.txt",
"/b.txt",
"/b/c.txt",
"/b/d/e.txt",
"/bz.txt",
"/u/tobie/x.png"
] {
file::put(type::file("docs", $key), "x");
};
file::list("docs", { prefix: "/b" }).map(|$e| file::key($e.file));
//- ['/b.txt', '/b/c.txt', '/b/d/e.txt', '/bz.txt']
file::list("docs", { prefix: "/b/" }).map(|$e| file::key($e.file));
//- ['/b/c.txt', '/b/d/e.txt']A prefix cannot contain a . or .. segment, so a listing cannot reach outside the bucket.
Folders
Without delimiter, a listing is recursive and returns every file below the prefix. With delimiter: "/", it returns the files directly below the prefix, and each folder below the prefix once, as an object with a single prefix field. A folder is reported only when it holds at least one file. The value of prefix can be passed to the next call to list that folder.
file::list("docs", { delimiter: "/" });[
{
etag: '"0"',
file: f"docs:/a.txt",
size: 1,
updated: d'2026-11-02T03:27:17.784772Z'
},
{
etag: '"1"',
file: f"docs:/b.txt",
size: 1,
updated: d'2026-11-02T03:27:17.784783Z'
},
{
prefix: '/b/'
},
{
etag: '"4"',
file: f"docs:/bz.txt",
size: 1,
updated: d'2026-11-02T03:27:17.784788Z'
},
{
prefix: '/u/'
}
]file::list("docs", { prefix: "/b/", delimiter: "/" })
.map(|$e| $e.prefix ?? file::key($e.file));['/b/c.txt', '/b/d/']Pages
start and limit together return a listing one page at a time. The last entry of a page is the start of the next page, whether it is a file or a folder. start also accepts the file field of an entry without a change.
LET $keys = |$opts| file::list("docs", $opts)
.map(|$e| $e.prefix ?? file::key($e.file));
$keys({ delimiter: "/", limit: 3 });
//- ['/a.txt', '/b.txt', '/b/']
$keys({ delimiter: "/", start: "/b/", limit: 3 });
//- ['/bz.txt', '/u/']
$keys({ start: file::list("docs", { limit: 2 })[1].file, limit: 2 });
//- ['/b/c.txt', '/b/d/e.txt'] file::presign_get
Available since: v4.0.0
file::presign_get(file, $options: option<object>) -> objectThe file::presign_get function returns a signed URL that downloads a file over the /bucket HTTP route without a SurrealDB session, for example as the src of an <img> or <video> element. The server must have a signing secret in SURREAL_BUCKET_PRESIGN_SECRET; without one, the function fails. See Presigned URLs for the setup and the security model.
The optional second argument is an object with one field:
expires(duration): how long the URL works. The default is15m, the minimum is1s, and the maximum is the server'sSURREAL_BUCKET_PRESIGN_MAX_TTL, which defaults to1h.
Any other field is an error, so a misspelled option cannot give a URL more access than was requested. The function checks the bucket's PERMISSIONS clause with $action set to presign_get, and refuses for a bucket whose clause does not allow it.
DEFINE BUCKET images BACKEND "memory";
file::presign_get(f"images:/cover.png", { expires: 10m });{
content_type: NONE,
expires: d'2026-11-03T23:13:50Z',
href: '/ns/main/db/main/bucket/images/cover.png?exp=1793747630&sig=rZGAmzLATj5SPisQxRb0Bj6t1IH3eGh2Euv1qYCjnv0',
method: 'GET',
origin: NONE,
path: '/ns/main/db/main/bucket/images/cover.png?exp=1793747630&sig=rZGAmzLATj5SPisQxRb0Bj6t1IH3eGh2Euv1qYCjnv0'
}The returned object has these fields:
path(string): the path and query string of the URL. It is always relative.origin(option<string>): the value ofSURREAL_PUBLIC_ORIGIN, orNONEwhen the server does not have one.href(string):originfollowed bypath, orpathalone when there is no origin.method(string): the HTTP method the URL accepts,GEThere.expires(datetime): the time after which the URL stops working.content_type(option<string>): alwaysNONEfor a download URL.
The method form f"images:/cover.png".presign_get() gives the same result.
file::presign_put
Available since: v4.0.0
file::presign_put(file, $options: option<object>) -> objectThe file::presign_put function returns a signed URL that uploads a file with an HTTP PUT request to the /bucket HTTP route, without a SurrealDB session. The result has the same fields as file::presign_get, with method set to PUT.
The optional second argument is an object with these fields:
expires(duration): how long the URL works, as forfile::presign_get.max_size(int): the largest upload in bytes that the URL accepts. The default and the maximum are the server'sSURREAL_BUCKET_PRESIGN_MAX_SIZE, which defaults to 5 GiB.overwrite(bool): whether the upload can replace an existing file. The default istrue. Withfalse, an upload to a key that holds a file fails with412 Precondition Failed.content_type(string): the media type the upload must declare in itsContent-Typeheader. A URL created without one accepts only an upload with noContent-Typeheader, or withapplication/octet-stream. Afile:bucket cannot store a content type, so the function fails when this option is set for one.
DEFINE BUCKET images BACKEND "memory";
file::presign_put(f"images:/u/avatar.png", {
expires: 5m,
max_size: 1048576,
overwrite: false,
content_type: "image/png"
});{
content_type: 'image/png',
expires: d'2026-11-03T23:08:50Z',
href: '/ns/main/db/main/bucket/images/u/avatar.png?ct=image%2Fpng&exp=1793747330&max=1048576&ow=false&sig=WkC3AGb8rLJr0ZgWzv0vrYu67ulUN4iKbOdPI7513bE',
method: 'PUT',
origin: NONE,
path: '/ns/main/db/main/bucket/images/u/avatar.png?ct=image%2Fpng&exp=1793747330&max=1048576&ow=false&sig=WkC3AGb8rLJr0ZgWzv0vrYu67ulUN4iKbOdPI7513bE'
}The function checks the bucket's PERMISSIONS clause with $action set to presign_put, and with the options bound as $expires, $max_size, $overwrite and $content_type, so the clause can limit what a URL allows. A READONLY bucket never returns an upload URL. See Setting permissions on buckets.
file::put
The file::put function adds data into a file, overwriting any existing data.
file::put(file, value, $options: option<object>)An example of this function followed by file::get() to display the contents:
DEFINE BUCKET my_bucket BACKEND "memory";
f"my_bucket:/my_book.txt".put("Once there were four children whose names were Peter, Susan...");
f"my_bucket:/my_book.txt".put("Or were there? I don't quite remember.");
<string>f"my_bucket:/my_book.txt".get();"Or were there? I don't quite remember."Content types
Available since: v4.0.0
The optional third argument is an object with a single field, content_type, which stores a media type with the file. file::head returns it, and the /bucket HTTP route sends it as the Content-Type of the file.
DEFINE BUCKET images BACKEND "memory";
file::put(f"images:/a.png", <bytes>"PNG", { content_type: "image/png" });
file::head(f"images:/a.png").content_type;
//- 'image/png'
-- A type is never taken from the file name
file::put(f"images:/b.png", <bytes>"PNG");
file::head(f"images:/b.png").content_type;
//- NONEA write without content_type removes the type that the file had before. application/octet-stream is the same as no type. A value that is not a valid media type, or any other field in the object, is an error. A copy keeps the type, and a copy to another bucket fails if that bucket cannot store it.
The memory backend and the cloud object storage backends store a content type. A file: bucket cannot, and file::put with content_type fails on one, rather than writing the file without its type.
file::put_if_not_exists
The file::put_if_not_exists function adds data into a file, unless a file of the same name already exists. It takes the same content_type option as file::put.
file::put_if_not_exists(file, value, $options: option<object>)An example of this function followed by file::get() to display the contents:
DEFINE BUCKET my_bucket BACKEND "memory";
-- Creates file and adds data
f"my_bucket:/my_book.txt".put_if_not_exists("Once there were four children whose names were Peter, Susan...");
-- Does nothing
f"my_bucket:/my_book.txt".put_if_not_exists("Or were there? I don't quite remember.");
<string>f"my_bucket:/my_book.txt".get();'Once there were four children whose names were Peter, Susan...' file::rename
The file::rename function renames a file, overwriting any existing file that has the same name as the target name.
file::rename()An example of a file being renamed over an existing file:
DEFINE BUCKET my_bucket BACKEND "memory";
f"my_bucket:/my_book.txt".put("Once there were four children whose names were Peter, Susan...");
f"my_bucket:/other_book.txt".put("Or were there? I don't quite remember.");
-- Rename to my_book.txt, overwriting existing file of the same name
f"my_bucket:/other_book.txt".rename("my_book.txt");
<string>f"my_bucket:/my_book.txt".get();"Or were there? I don't quite remember." file::rename_if_not_exists
The file::rename_if_not_exists function renames a file, returning an error if a file already exists that has the same name as the target name.
file::rename_if_not_exists()-------- Query --------
'Operation for bucket `my_bucket` failed: Object at location my_book.txt already exists:
Object already exists at that location: my_book.txt'
-------- Query --------
'Once there were four children whose names were Peter, Susan...