Upload a file
Upload an object to a bucket. Accepts a File/IO, a Pathname, or a raw String body.
Upload bytes to path inside the bucket scoped by from(bucket_id). The bytes source can be a File/IO, a Pathname (read from disk), or a raw String payload — whichever fits the call site best.
Signature
supabase.storage.from(bucket_id).upload(path,file,content_type: nil,cache_control: nil,upsert: false,metadata: nil,headers: nil)Parameters
| Name | Type | Required | Description |
|---|---|---|---|
path | String | Required | Destination path within the bucket (e.g. "folder/avatar.png"). A leading "/" is stripped. |
file | String, IO, Pathname | Required | Bytes source. A String is treated as raw bytes (NOT a filesystem path — see the callout below). An IO/File or Pathname is read. |
content_type | String | Optional | MIME type sent as part of the multipart body. Defaults to text/plain;charset=UTF-8 when omitted — set it explicitly for binary uploads. |
cache_control | String, Integer | Optional | Cache lifetime in seconds. Sent as Cache-Control: max-age=<n>. Defaults to "3600" (raw, no max-age wrapper) when omitted. |
upsert | Boolean | Optional | true to overwrite an existing object at path. Sent as the x-upsert header. Defaults to false. |
metadata | Hash | Optional | Arbitrary metadata. JSON-encoded then base64-encoded into the x-metadata header AND repeated as a form field. |
headers | Hash | Optional | Extra HTTP headers to merge into the multipart request (e.g. custom auth bearer overrides). |
Returns
Struct with path (the relative path you uploaded to), full_path (alias fullPath) carrying the bucket-prefixed key returned by storage-api, and key (same value as full_path).
Example — File.open body
File.open("./ada.png", "rb") do |io|
response = supabase.storage.from("avatars").upload(
"people/ada.png",
io,
content_type: "image/png",
cache_control: 31_536_000, # one year
upsert: true
)
response.path # => "people/ada.png"
response.full_path # => "avatars/people/ada.png"
endExample — raw String body
# A String is uploaded as raw bytes — it is NOT treated as a file path.
supabase.storage.from("notes").upload(
"scratch/hello.txt",
"hello world\n",
content_type: "text/plain"
)Example — Pathname body
require "pathname"
supabase.storage.from("avatars").upload(
"people/grace.png",
Pathname("./grace.png"),
content_type: "image/png"
)Example — with custom metadata
supabase.storage.from("invoices").upload(
"2026/Q2/INV-0001.pdf",
File.binread("INV-0001.pdf"),
content_type: "application/pdf",
metadata: { customer_id: 42, locked: true }
)Strings are bytes, not paths
A String is always raw bytes/text. Disk reads are spelled File.open(path), File.binread(path), or Pathname(path). This matches supabase-js (Blob/Buffer/File, never a path string) and avoids the silent footgun where upload("a.png", "./a.png") would upload the literal path string.