mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat: allow 500 GB uploads (#37120)
* feat: allow 500 GB uploads * upsell * docs + pricing page * Update StorageSettings.tsx * Small alignment fix --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
1efa09cf54
commit
eaa2f2e68b
6 files changed
+51
-22
No files matched your search
@@ -8,13 +8,13 @@ sidebar_label: 'Limits'
|
||||
|
||||
## Global file size
|
||||
|
||||
You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 50 GB. If you need more than 50 GB, [contact us](https://supabase.com/dashboard/support/new).
|
||||
You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 500 GB. If you need more than 500 GB, [contact us](https://supabase.com/dashboard/support/new).
|
||||
|
||||
| Plan | Max File Size Limit |
|
||||
| ---------- | ------------------- |
|
||||
| Free | 50 MB |
|
||||
| Pro | 50 GB |
|
||||
| Team | 50 GB |
|
||||
| Pro | 500 GB |
|
||||
| Team | 500 GB |
|
||||
| Enterprise | Custom |
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
@@ -17,7 +17,7 @@ The S3 protocol supports file upload using:
|
||||
|
||||
The `PutObject` action uploads the file in a single request. This matches the behavior of the Supabase SDK [Standard Upload](/docs/guides/storage/uploads/standard-uploads).
|
||||
|
||||
Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 50 GB.
|
||||
Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 500 GB.
|
||||
|
||||
For example, using JavaScript and the `aws-sdk` client:
|
||||
|
||||
@@ -42,7 +42,7 @@ await s3Client.send(uploadCommand)
|
||||
|
||||
Multipart Uploads split the file into smaller parts and upload them in parallel, maximizing the upload speed on a fast network. When uploading large files, this allows you to retry the upload of individual parts in case of network issues.
|
||||
|
||||
This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 50 GB.
|
||||
This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 500 GB.
|
||||
|
||||
### Upload a file in parts
|
||||
|
||||
|
||||
+5
-1
@@ -1,4 +1,8 @@
|
||||
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 50 * 1024 * 1024 * 1024 // 50 GB
|
||||
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN = 50 * 1024 * 1024 // 50 MB
|
||||
/** Max bytes for capped storage (i.e. Pro with spend cap) */
|
||||
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED = 50 * 1024 * 1024 * 1024 // 50 GB
|
||||
/** Max bytes for uncapped storage (i.e. Pro without spend cap, Team, Enterprise) */
|
||||
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED = 500 * 1024 * 1024 * 1024 // 500 GB
|
||||
|
||||
export enum StorageSizeUnits {
|
||||
BYTES = 'bytes',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { Clock } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { SubmitHandler, useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import * as z from 'zod'
|
||||
@@ -16,6 +15,7 @@ import { useProjectStorageConfigQuery } from 'data/config/project-storage-config
|
||||
import { useProjectStorageConfigUpdateUpdateMutation } from 'data/config/project-storage-config-update-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { formatBytes } from 'lib/helpers'
|
||||
import {
|
||||
Button,
|
||||
FormControl_Shadcn_,
|
||||
@@ -32,7 +32,12 @@ import {
|
||||
Select_Shadcn_,
|
||||
Switch,
|
||||
} from 'ui'
|
||||
import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants'
|
||||
import {
|
||||
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED,
|
||||
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN,
|
||||
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED,
|
||||
StorageSizeUnits,
|
||||
} from './StorageSettings.constants'
|
||||
import { convertFromBytes, convertToBytes } from './StorageSettings.utils'
|
||||
|
||||
interface StorageSettingsState {
|
||||
@@ -56,6 +61,8 @@ const StorageSettings = () => {
|
||||
|
||||
const organization = useSelectedOrganization()
|
||||
const isFreeTier = organization?.plan.id === 'free'
|
||||
const isSpendCapOn =
|
||||
organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false
|
||||
|
||||
const [initialValues, setInitialValues] = useState<StorageSettingsState>({
|
||||
fileSizeLimit: 0,
|
||||
@@ -84,9 +91,17 @@ const StorageSettings = () => {
|
||||
}
|
||||
}, [isSuccess, config])
|
||||
|
||||
const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(
|
||||
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES
|
||||
)} bytes`
|
||||
const maxBytes = useMemo(() => {
|
||||
if (organization?.plan.id === 'free') {
|
||||
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN
|
||||
} else if (organization?.usage_billing_enabled) {
|
||||
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED
|
||||
} else {
|
||||
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED
|
||||
}
|
||||
}, [organization])
|
||||
|
||||
const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(maxBytes)} bytes`
|
||||
|
||||
const FormSchema = z
|
||||
.object({
|
||||
@@ -96,7 +111,7 @@ const StorageSettings = () => {
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const { unit, fileSizeLimit } = data
|
||||
const { value: formattedMaxLimit } = convertFromBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, unit)
|
||||
const { value: formattedMaxLimit } = convertFromBytes(maxBytes, unit)
|
||||
|
||||
if (fileSizeLimit > formattedMaxLimit) {
|
||||
ctx.addIssue({
|
||||
@@ -156,7 +171,7 @@ const StorageSettings = () => {
|
||||
</div>
|
||||
|
||||
<div className="relative flex flex-col col-span-12 gap-x-6 gap-y-2 lg:col-span-8">
|
||||
<div className="grid grid-cols-12 col-span-12 gap-2 items-center">
|
||||
<div className="grid grid-cols-12 col-span-12 gap-2 items-start">
|
||||
<div className="col-span-8">
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
@@ -222,8 +237,7 @@ const StorageSettings = () => {
|
||||
limit,
|
||||
storageUnit
|
||||
).toLocaleString()} bytes. `}
|
||||
Maximum size in bytes of a file that can be uploaded is 50 GB (
|
||||
{formattedMaxSizeBytes}).
|
||||
Maximum upload file size is {formatBytes(maxBytes)}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -261,9 +275,18 @@ const StorageSettings = () => {
|
||||
{isFreeTier && (
|
||||
<div className="px-6 pb-6">
|
||||
<UpgradeToPro
|
||||
icon={<Clock size={14} className="text-foreground-muted" />}
|
||||
primaryText="Free Plan has a fixed upload file size limit of 50 MB."
|
||||
secondaryText="Upgrade to the Pro Plan for a configurable upload file size limit of up to 50 GB."
|
||||
secondaryText={`Upgrade to Pro Plan for a configurable upload file size limit of ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)} and unlock image transformations.`}
|
||||
source="storageSizeLimit"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isSpendCapOn && (
|
||||
<div className="px-6 pb-6">
|
||||
<UpgradeToPro
|
||||
buttonText="Disable Spend Cap"
|
||||
primaryText="Reduced max upload file size limit due to Spend Cap"
|
||||
secondaryText={`Disable your Spend Cap to allow file uploads of up to ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)}.`}
|
||||
source="storageSizeLimit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@ const UpgradeToPro = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
Reset database password
|
||||
{buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')}
|
||||
</ButtonTooltip>
|
||||
) : (
|
||||
<Button
|
||||
@@ -81,7 +81,9 @@ const UpgradeToPro = ({
|
||||
href={
|
||||
plan === 'free'
|
||||
? `/org/${organization?.slug ?? '_'}/billing?panel=subscriptionPlan&source=${source}`
|
||||
: `/project/${project?.ref ?? '_'}/settings/addons?panel=${addon}&source=${source}`
|
||||
: addon == null
|
||||
? `/org/${organization?.slug ?? '_'}/billing?panel=costControl&source=${source}`
|
||||
: `/project/${project?.ref ?? '_'}/settings/addons?panel=${addon}&source=${source}`
|
||||
}
|
||||
>
|
||||
{buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')}
|
||||
|
||||
@@ -421,8 +421,8 @@ export const pricing: Pricing = {
|
||||
title: 'Max file upload size',
|
||||
plans: {
|
||||
free: '50 MB',
|
||||
pro: '50 GB',
|
||||
team: '50 GB',
|
||||
pro: '500 GB',
|
||||
team: '500 GB',
|
||||
enterprise: 'Custom',
|
||||
},
|
||||
usage_based: false,
|
||||
|
||||
Reference in new issue
Block a user