Upload, click Upload and pick files, or drag them onto the file list. Folder uploads are supported on browsers that allow it.
New folder, click New folder to create a path prefix. Note: empty folders are a dashboard convenience; once you upload an object below them, the folder is real on the object store.
Download, click a file’s overflow menu and choose Download. Large files stream.
Delete, single-file via overflow menu, or select multiple files and click Delete for bulk delete (up to 100 at a time).
Search, type into the search box to filter the current prefix.
A presigned URL is a temporary, signed URL that lets the holder upload or download an object without your credentials being involved. Generate it server-side, hand it to a browser, let the browser talk to the bucket directly.
import { PutObjectCommand } from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";const uploadUrl = await getSignedUrl( s3, new PutObjectCommand({ Bucket: "my-bucket", Key: `uploads/${userId}/${Date.now()}-${filename}`, ContentType: mimeType, }), { expiresIn: 60 * 5 } // 5 minutes);// Browser does the actual upload:// await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": mimeType }, body: file });
Keep expiries short. A 5-minute upload window and a 15-minute download window cover most flows. Longer expiries widen the time a leaked URL can be used.
For files larger than ~100 MB, use multipart upload. The S3 SDKs handle this for you (@aws-sdk/lib-storage’s Upload class in Node, upload_file in boto3); use those instead of the lower-level multipart APIs.
import { Upload } from "@aws-sdk/lib-storage";import { createReadStream } from "fs";const upload = new Upload({ client: s3, params: { Bucket: "my-bucket", Key: "video/long-cut.mp4", Body: createReadStream("./long-cut.mp4"), ContentType: "video/mp4", }, partSize: 10 * 1024 * 1024, // 10 MB per part queueSize: 4,});upload.on("httpUploadProgress", (p) => console.log(p.loaded, "/", p.total));await upload.done();
Part size minimum is 5 MB (S3 standard), part size maximum is 5 GB. Aborted multipart uploads leave orphan parts; the SDKs clean these up automatically on retry.
There are no real directories. A path like reports/2026/q1.pdf is one object with a key that contains slashes; “the folder reports/2026/” is just every object whose key starts with that prefix. Creating an empty folder via the dashboard puts a placeholder marker; uploading any object under that prefix removes the placeholder.
Objects are private. The only way to read or write a bucket is with a valid storage credential, no per-object public toggle, no anonymous public-read URLs. Two ways to hand out access:
Server-side, with an Editor or ReadOnly credential. Embed the credential in your app and use the S3 SDK directly.
To a browser, with a presigned URL. Generate a short-lived signed GET or PUT server-side and hand it to the client. See Presigned URLs.
To make a static asset reachable from a browser without a presigned URL, put a CDN or your application in front of the bucket and serve the bytes through that. See Storage credentials for the role split.
AccessDenied from an S3 client. Most likely the credentials are wrong, expired, or scoped read-only for a write call. Re-check the access key pair, and confirm the credential’s role under Storage credentials.SignatureDoesNotMatch. Region mismatch, the bucket’s region must match what you configured on the S3 client. Copy the region from the bucket detail page.NoSuchBucket. The bucket name in the URL doesn’t exist in this region. Double-check forcePathStyle: true so the bucket name lives in the URL path, not the hostname.Presigned URL “expired” before the user uploaded. Lengthen the expiresIn window, or generate the URL closer to when the user actually uploads (typically after they pick the file).Multipart upload stuck. Aborts happen client-side. Use the SDK’s high-level Upload / upload_file instead of stitching multipart parts yourself; those handle retries and aborts for you.