Skip to content

Functions

/

Database functions

File

Available since: v3.0.0

File functions

These functions can be used to work with files.

Warning

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

API DEFINITION
file::bucket(file) -> string

The 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);
Output
'my_bucket'

The counterpart to this function is file::key, which returns the latter part of a file pointer.

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.

API DEFINITION
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");


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.

API DEFINITION
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");
Output
'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'


The file::delete function deletes a file.

API DEFINITION
file::delete(string)

Example of a file my_book.txt being deleted:

f"my_bucket:/my_book.txt".delete();


The file::exists function checks to see if a file exists at the path and file name indicated.

API DEFINITION
file::exists(string) -> bool

Example 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")
};


The file::get function retrieves a file for use.

API DEFINITION
file::get(string) -> bytes

A 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();
Output
-------- Query --------

b"536F6D6520636F6E74656E74"

-------- Query --------

'Once there were four children whose names were Peter, Susan...'


The file::head function returns the metadata for a file.

API DEFINITION
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 a file: bucket it is a weak identifier that starts with W/, 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 to file::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();
Sample output
{
	content_type: 'image/png',
	etag: '"0"',
	file: f"my_bucket:/cover.png",
	size: 3,
	updated: d'2026-11-02T10:15:04.221917Z'
}


API DEFINITION
file::key(file) -> string

The 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);
Output
'/file_name'

The counterpart to this function is file::bucket, which returns the bucket name of a file pointer.

API DEFINITION
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 that file::head returns.

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");
Sample output
[
	{
		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 (string or file): returns only the entries after this key. The entry for start itself 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.

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.

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: "/" });
Sample output
[
	{
		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));
Output
['/b/c.txt', '/b/d/']

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']

Available since: v4.0.0

API DEFINITION
file::presign_get(file, $options: option<object>) -> object

The 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 is 15m, the minimum is 1s, and the maximum is the server's SURREAL_BUCKET_PRESIGN_MAX_TTL, which defaults to 1h.

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 });
Sample output
{
	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 of SURREAL_PUBLIC_ORIGIN, or NONE when the server does not have one.

  • href (string): origin followed by path, or path alone when there is no origin.

  • method (string): the HTTP method the URL accepts, GET here.

  • expires (datetime): the time after which the URL stops working.

  • content_type (option<string>): always NONE for a download URL.

The method form f"images:/cover.png".presign_get() gives the same result.

Available since: v4.0.0

API DEFINITION
file::presign_put(file, $options: option<object>) -> object

The 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 for file::presign_get.

  • max_size (int): the largest upload in bytes that the URL accepts. The default and the maximum are the server's SURREAL_BUCKET_PRESIGN_MAX_SIZE, which defaults to 5 GiB.

  • overwrite (bool): whether the upload can replace an existing file. The default is true. With false, an upload to a key that holds a file fails with 412 Precondition Failed.

  • content_type (string): the media type the upload must declare in its Content-Type header. A URL created without one accepts only an upload with no Content-Type header, or with application/octet-stream. A file: 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"
});
Sample output
{
	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.

The file::put function adds data into a file, overwriting any existing data.

API DEFINITION
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();
Output
"Or were there? I don't quite remember."

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;
//- NONE

A 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.


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.

API DEFINITION
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();
Output
'Once there were four children whose names were Peter, Susan...'


The file::rename function renames a file, overwriting any existing file that has the same name as the target name.

API DEFINITION
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();
Output
"Or were there? I don't quite remember."


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.

API DEFINITION
file::rename_if_not_exists()
Output
-------- 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...

Was this page helpful?